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