]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.akb/src/main/java/org/argeo/slc/akb/utils/AkbJcrUtils.java
JDBC, SSH command (+ pseudo file retrieval) and keyring
[gpl/argeo-slc.git] / runtime / org.argeo.slc.akb / src / main / java / org / argeo / slc / akb / utils / AkbJcrUtils.java
1 package org.argeo.slc.akb.utils;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.TreeMap;
7
8 import javax.jcr.Node;
9 import javax.jcr.NodeIterator;
10 import javax.jcr.Property;
11 import javax.jcr.PropertyIterator;
12 import javax.jcr.Repository;
13 import javax.jcr.RepositoryException;
14 import javax.jcr.Session;
15 import javax.jcr.Value;
16 import javax.jcr.query.QueryManager;
17 import javax.jcr.query.QueryResult;
18 import javax.jcr.query.Row;
19 import javax.jcr.query.RowIterator;
20 import javax.jcr.query.qom.Constraint;
21 import javax.jcr.query.qom.Ordering;
22 import javax.jcr.query.qom.QueryObjectModel;
23 import javax.jcr.query.qom.QueryObjectModelFactory;
24 import javax.jcr.query.qom.Selector;
25
26 import org.argeo.jcr.JcrUtils;
27 import org.argeo.jcr.PropertyDiff;
28 import org.argeo.slc.akb.AkbException;
29 import org.argeo.slc.akb.AkbTypes;
30
31 /** Some static utils methods that might be factorized in a near future */
32 public class AkbJcrUtils {
33
34 // /////////////////////////
35 // SPECIFIC METHOS
36 /**
37 * Return the type of alias that must be used given current item type
38 */
39 public static String getAliasTypeForNode(Node itemTemplate) {
40 try {
41 if (itemTemplate.isNodeType(AkbTypes.AKB_JDBC_QUERY))
42 return AkbTypes.AKB_JDBC_CONNECTOR;
43 else if (itemTemplate.isNodeType(AkbTypes.AKB_SSH_COMMAND)
44 || itemTemplate.isNodeType(AkbTypes.AKB_SSH_FILE))
45 return AkbTypes.AKB_SSH_CONNECTOR;
46 else
47 throw new AkbException("No connector type define for node "
48 + itemTemplate);
49 } catch (RepositoryException re) {
50 throw new AkbException("Unable to login", re);
51 }
52 }
53
54 /**
55 * Return defined alias in the current environment given current item type
56 */
57 public static List<Node> getDefinedAliasForNode(Node itemTemplate) {
58 try {
59 Session session = itemTemplate.getSession();
60 QueryManager queryManager = session.getWorkspace()
61 .getQueryManager();
62 QueryObjectModelFactory factory = queryManager.getQOMFactory();
63 String nodeType = getAliasTypeForNode(itemTemplate);
64
65 Selector source = factory.selector(nodeType, nodeType);
66 String basePath = getCurrentEnvBasePath(itemTemplate);
67 Constraint defaultC = factory.descendantNode(
68 source.getSelectorName(), basePath);
69
70 // Order by default by JCR TITLE
71 // TODO check if node definition has MIX_TITLE mixin
72 // TODO Apparently case insensitive ordering is not implemented in
73 // current used JCR implementation
74 Ordering order = factory
75 .ascending(factory.upperCase(factory.propertyValue(
76 source.getSelectorName(), Property.JCR_TITLE)));
77 QueryObjectModel query;
78 query = factory.createQuery(source, defaultC,
79 new Ordering[] { order }, null);
80 QueryResult result = query.execute();
81
82 NodeIterator ni = result.getNodes();
83
84 return JcrUtils.nodeIteratorToList(ni);
85 } catch (RepositoryException e) {
86 throw new AkbException("Unable to list connector", e);
87 }
88 }
89
90 /**
91 * Return the current env base path
92 */
93 public static String getCurrentEnvBasePath(Node akbNode) {
94 try {
95 if (akbNode.getDepth() == 0)
96 // no base path for root node
97 return null;
98
99 Node parNode = akbNode.getParent();
100
101 while (parNode != null)
102 if (akbNode.isNodeType(AkbTypes.AKB_ENV)
103 || akbNode.isNodeType(AkbTypes.AKB_ENV_TEMPLATE))
104 return akbNode.getPath();
105 else if (parNode.getDepth() == 0)
106 // we found not fitting node
107 return null;
108 else {
109 akbNode = parNode;
110 parNode = parNode.getParent();
111 }
112 return null;
113 } catch (RepositoryException re) {
114 throw new AkbException("Unable to login", re);
115 }
116 }
117
118 // //////////////////////////////////
119 // METHODS THAT CAN BE FACTORIZED
120 /**
121 * Call {@link Repository#login()} without exceptions (useful in super
122 * constructors and dependency injection).
123 */
124 public static Session login(Repository repository) {
125 try {
126 return repository.login();
127 } catch (RepositoryException re) {
128 throw new AkbException("Unable to login", re);
129 }
130 }
131
132 /**
133 * Convert a {@link rowIterator} to a list of {@link Node} given a selector
134 * name. It relies on the <code>Row.getNode(String selectorName)</code>
135 * method.
136 */
137 public static List<Node> rowIteratorToList(RowIterator rowIterator,
138 String selectorName) throws RepositoryException {
139 List<Node> nodes = new ArrayList<Node>();
140 while (rowIterator.hasNext()) {
141 Row row = rowIterator.nextRow();
142 if (row.getNode(selectorName) != null)
143 nodes.add(row.getNode(selectorName));
144 }
145 return nodes;
146 }
147
148 /**
149 * Check if a string is null or an empty string (a string with only spaces
150 * is considered as empty
151 */
152 public static boolean isEmptyString(String stringToTest) {
153 return stringToTest == null || "".equals(stringToTest.trim());
154 }
155
156 /**
157 * Check if a string is null or an empty string (a string with only spaces
158 * is considered as empty
159 */
160 public static boolean checkNotEmptyString(String string) {
161 return string != null && !"".equals(string.trim());
162 }
163
164 /**
165 * Wraps the versionMananger.isCheckedOut(path) method to adapt it to the
166 * current check in / check out policy.
167 *
168 * TODO : add management of check out by others.
169 */
170 public static boolean isNodeCheckedOut(Node node) {
171 try {
172 return node.getSession().getWorkspace().getVersionManager()
173 .isCheckedOut(node.getPath());
174 } catch (RepositoryException re) {
175 throw new AkbException("Unable to get check out status for node",
176 re);
177 }
178 }
179
180 /**
181 * For the time being, same as isNodeCheckedOut(Node node).
182 *
183 * TODO : add management of check out by others.
184 */
185 public static boolean isNodeCheckedOutByMe(Node node) {
186 return isNodeCheckedOut(node);
187 }
188
189 /**
190 * Wraps the versionMananger.checkedOut(path) method to adapt it to the
191 * current check in / check out policy.
192 *
193 * TODO : add management of check out by others.
194 */
195 public static void checkout(Node node) {
196 try {
197 node.getSession().getWorkspace().getVersionManager()
198 .checkout(node.getPath());
199 } catch (RepositoryException re) {
200 throw new AkbException("Unable to check out Node", re);
201 }
202 }
203
204 /**
205 * Wraps the versionMananger.checkedIn(path) method to adapt it to the
206 * current check in / check out policy.
207 *
208 * It also checked if the current entity has to be moved or not. TODO : add
209 * management of check out by others.
210 */
211 public static void saveAndCheckin(Node node) {
212 try {
213 JcrUtils.updateLastModified(node);
214 node.getSession().save();
215 node.getSession().getWorkspace().getVersionManager()
216 .checkin(node.getPath());
217 } catch (RepositoryException re) {
218 throw new AkbException("Unable to save and chek in node", re);
219 }
220 }
221
222 /**
223 * Wraps the versionMananger.checkedIn(path) method to adapt it to the
224 * current check in / check out policy.
225 *
226 * TODO : add management of check out by others. TODO : manage usecase where
227 * a node that has never been checked in (draft node) is canceled and thus
228 * must be deleted
229 */
230 public static void cancelAndCheckin(Node node) {
231 try {
232 String path = node.getPath();
233 Session session = node.getSession();
234 JcrUtils.discardUnderlyingSessionQuietly(node);
235 // if the node has never been saved, it does not exist anymore.
236 if (session.nodeExists(path))
237 session.getWorkspace().getVersionManager().checkin(path);
238 } catch (RepositoryException re) {
239 throw new AkbException("Unable to save and chek in node", re);
240 }
241 }
242
243 /**
244 * Concisely get the string value of a property. It returns an empty String
245 * rather than null if this node doesn't have this property or if the
246 * corresponding property is an empty string.
247 */
248 public static String get(Node node, String propertyName) {
249 try {
250 if (!node.hasProperty(propertyName))
251 return "";
252 else
253 return node.getProperty(propertyName).getString();
254 } catch (RepositoryException e) {
255 throw new AkbException("Cannot get property " + propertyName
256 + " of " + node, e);
257 }
258 }
259
260 /**
261 * Concisely get the value of a property or null if this node doesn't have
262 * this property
263 */
264 public static Boolean getBooleanValue(Node node, String propertyName) {
265 try {
266 if (!node.hasProperty(propertyName))
267 return null;
268 else
269 return node.getProperty(propertyName).getBoolean();
270 } catch (RepositoryException e) {
271 throw new AkbException("Cannot get boolean property "
272 + propertyName + " of " + node, e);
273 }
274 }
275
276 /**
277 * Concisely get the identifier of a node in Ui listener for instance
278 * */
279 public static String getIdentifierQuietly(Node node) {
280 try {
281 return node.getIdentifier();
282 } catch (RepositoryException e) {
283 throw new AkbException("Cannot get identifier for node " + node, e);
284 }
285 }
286
287 public static Map<String, PropertyDiff> diffProperties(Node reference,
288 Node observed) {
289 Map<String, PropertyDiff> diffs = new TreeMap<String, PropertyDiff>();
290 diffPropertiesLevel(diffs, null, reference, observed);
291 return diffs;
292 }
293
294 /**
295 * Compare the properties of two nodes. Extends
296 * <code>JcrUtils.diffPropertiesLevel</code> to also track differences in
297 * multiple value properties and sub graph. No property is skipped (among
298 * other all technical jcr:... properties) to be able to track jcr:title and
299 * description properties, among other. Filtering must be applied afterwards
300 * to only keep relevant properties.
301 */
302 static void diffPropertiesLevel(Map<String, PropertyDiff> diffs,
303 String baseRelPath, Node reference, Node observed) {
304 try {
305 // check removed and modified
306 PropertyIterator pit = reference.getProperties();
307 while (pit.hasNext()) {
308 Property p = pit.nextProperty();
309 String name = p.getName();
310 // if (name.startsWith("jcr:"))
311 // continue props;
312
313 if (!observed.hasProperty(name)) {
314 String relPath = propertyRelPath(baseRelPath, name);
315 PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED,
316 relPath, p.getValue(), null);
317 diffs.put(relPath, pDiff);
318 } else {
319 if (p.isMultiple()) {
320 int i = 0;
321
322 Value[] refValues = p.getValues();
323 Value[] newValues = observed.getProperty(name)
324 .getValues();
325 String relPath = propertyRelPath(baseRelPath, name);
326 refValues: for (Value refValue : refValues) {
327 for (Value newValue : newValues) {
328 if (refValue.equals(newValue))
329 continue refValues;
330 }
331 PropertyDiff pDiff = new PropertyDiff(
332 PropertyDiff.REMOVED, relPath, refValue,
333 null);
334 diffs.put(relPath + "_" + i++, pDiff);
335 }
336
337 newValues: for (Value newValue : newValues) {
338 for (Value refValue : refValues) {
339 if (refValue.equals(newValue))
340 continue newValues;
341 }
342 PropertyDiff pDiff = new PropertyDiff(
343 PropertyDiff.ADDED, relPath, null, newValue);
344 diffs.put(relPath + "_" + i++, pDiff);
345 }
346
347 } else {
348 Value referenceValue = p.getValue();
349 Value newValue = observed.getProperty(name).getValue();
350 if (!referenceValue.equals(newValue)) {
351 String relPath = propertyRelPath(baseRelPath, name);
352 PropertyDiff pDiff = new PropertyDiff(
353 PropertyDiff.MODIFIED, relPath,
354 referenceValue, newValue);
355 diffs.put(relPath, pDiff);
356 }
357 }
358 }
359 }
360 // check added
361 pit = observed.getProperties();
362 // props:
363 while (pit.hasNext()) {
364 Property p = pit.nextProperty();
365 String name = p.getName();
366 // if (name.startsWith("jcr:"))
367 // continue props;
368 if (!reference.hasProperty(name)) {
369 String relPath = propertyRelPath(baseRelPath, name);
370 if (p.isMultiple()) {
371 Value[] newValues = observed.getProperty(name)
372 .getValues();
373 int i = 0;
374 for (Value newValue : newValues) {
375 PropertyDiff pDiff = new PropertyDiff(
376 PropertyDiff.ADDED, relPath, null, newValue);
377 diffs.put(relPath + "_" + i++, pDiff);
378 }
379 } else {
380 PropertyDiff pDiff = new PropertyDiff(
381 PropertyDiff.ADDED, relPath, null, p.getValue());
382 diffs.put(relPath, pDiff);
383 }
384 }
385 }
386 } catch (RepositoryException e) {
387 throw new AkbException("Cannot diff " + reference + " and "
388 + observed, e);
389 }
390 }
391
392 /** Builds a property relPath to be used in the diff. */
393 private static String propertyRelPath(String baseRelPath,
394 String propertyName) {
395 if (baseRelPath == null)
396 return propertyName;
397 else
398 return baseRelPath + '/' + propertyName;
399 }
400
401 /** prevent instantiation by others */
402 private AkbJcrUtils() {
403 }
404
405 }