1 package org
.argeo
.slc
.akb
.utils
;
3 import java
.util
.ArrayList
;
6 import java
.util
.TreeMap
;
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
;
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
;
33 /** Some static utils methods that might be factorized in a near future */
34 public class AkbJcrUtils
{
36 // /////////////////////////
39 * Return the type of alias that must be used given current item type
41 public static String
getAliasTypeForNode(Node itemTemplate
) {
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
;
49 throw new AkbException("No connector type define for node "
51 } catch (RepositoryException re
) {
52 throw new AkbException("Unable to login", re
);
57 * Return defined alias in the current environment given current item type
60 public static List
<Node
> getDefinedAliasForNode(Node itemTemplate
) {
62 Session session
= itemTemplate
.getSession();
63 QueryManager queryManager
= session
.getWorkspace()
65 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
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
);
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(
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
},
91 QueryResult result
= query
.execute();
93 NodeIterator ni
= result
.getNodes();
95 return JcrUtils
.nodeIteratorToList(ni
);
96 } catch (RepositoryException e
) {
97 throw new AkbException("Unable to list connector", e
);
102 * Return current template depending on the passed node
104 public static Node
getCurrentTemplate(Node akbNode
) {
106 if (akbNode
.getDepth() == 0)
107 // no base path for root node
109 Node parNode
= akbNode
.getParent();
111 while (parNode
!= null)
112 if (akbNode
.isNodeType(AkbTypes
.AKB_ENV_TEMPLATE
))
114 else if (parNode
.getDepth() == 0)
115 // we found not fitting node
119 parNode
= parNode
.getParent();
122 } catch (RepositoryException re
) {
123 throw new AkbException("Unable to find template for node "
129 * Return the current env base path
131 public static String
getCurrentEnvBasePath(Node akbNode
) {
133 if (akbNode
.getDepth() == 0)
134 // no base path for root node
137 Node parNode
= akbNode
.getParent();
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
148 parNode
= parNode
.getParent();
151 } catch (RepositoryException re
) {
152 throw new AkbException("Unable to login", re
);
156 // //////////////////////////////////
157 // METHODS THAT CAN BE FACTORIZED
159 * Call {@link Repository#login()} without exceptions (useful in super
160 * constructors and dependency injection).
162 public static Session
login(Repository repository
) {
164 return repository
.login();
165 } catch (RepositoryException re
) {
166 throw new AkbException("Unable to login", re
);
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>
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
));
187 * Check if a string is null or an empty string (a string with only spaces
188 * is considered as empty
190 public static boolean isEmptyString(String stringToTest
) {
191 return stringToTest
== null || "".equals(stringToTest
.trim());
195 * Check if a string is null or an empty string (a string with only spaces
196 * is considered as empty
198 public static boolean checkNotEmptyString(String string
) {
199 return string
!= null && !"".equals(string
.trim());
203 * Wraps the versionMananger.isCheckedOut(path) method to adapt it to the
204 * current check in / check out policy.
206 * TODO : add management of check out by others.
208 public static boolean isNodeCheckedOut(Node node
) {
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",
219 * For the time being, same as isNodeCheckedOut(Node node).
221 * TODO : add management of check out by others.
223 public static boolean isNodeCheckedOutByMe(Node node
) {
224 return isNodeCheckedOut(node
);
228 * Wraps the versionMananger.checkedOut(path) method to adapt it to the
229 * current check in / check out policy.
231 * TODO : add management of check out by others.
233 public static void checkout(Node node
) {
235 node
.getSession().getWorkspace().getVersionManager()
236 .checkout(node
.getPath());
237 } catch (RepositoryException re
) {
238 throw new AkbException("Unable to check out Node", re
);
243 * Wraps the versionMananger.checkedIn(path) method to adapt it to the
244 * current check in / check out policy.
246 * It also checked if the current entity has to be moved or not. TODO : add
247 * management of check out by others.
249 public static void saveAndCheckin(Node node
) {
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
);
261 * Wraps the versionMananger.checkedIn(path) method to adapt it to the
262 * current check in / check out policy.
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
268 public static void cancelAndCheckin(Node node
) {
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
);
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.
286 public static String
get(Node node
, String propertyName
) {
288 if (!node
.hasProperty(propertyName
))
291 return node
.getProperty(propertyName
).getString();
292 } catch (RepositoryException e
) {
293 throw new AkbException("Cannot get property " + propertyName
299 * Concisely get the value of a property or null if this node doesn't have
302 public static Boolean
getBooleanValue(Node node
, String propertyName
) {
304 if (!node
.hasProperty(propertyName
))
307 return node
.getProperty(propertyName
).getBoolean();
308 } catch (RepositoryException e
) {
309 throw new AkbException("Cannot get boolean property "
310 + propertyName
+ " of " + node
, e
);
315 * Concisely get the identifier of a node in Ui listener for instance
317 public static String
getIdentifierQuietly(Node node
) {
319 return node
.getIdentifier();
320 } catch (RepositoryException e
) {
321 throw new AkbException("Cannot get identifier for node " + node
, e
);
325 public static Map
<String
, PropertyDiff
> diffProperties(Node reference
,
327 Map
<String
, PropertyDiff
> diffs
= new TreeMap
<String
, PropertyDiff
>();
328 diffPropertiesLevel(diffs
, null, reference
, observed
);
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.
340 static void diffPropertiesLevel(Map
<String
, PropertyDiff
> diffs
,
341 String baseRelPath
, Node reference
, Node observed
) {
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:"))
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
);
357 if (p
.isMultiple()) {
360 Value
[] refValues
= p
.getValues();
361 Value
[] newValues
= observed
.getProperty(name
)
363 String relPath
= propertyRelPath(baseRelPath
, name
);
364 refValues
: for (Value refValue
: refValues
) {
365 for (Value newValue
: newValues
) {
366 if (refValue
.equals(newValue
))
369 PropertyDiff pDiff
= new PropertyDiff(
370 PropertyDiff
.REMOVED
, relPath
, refValue
,
372 diffs
.put(relPath
+ "_" + i
++, pDiff
);
375 newValues
: for (Value newValue
: newValues
) {
376 for (Value refValue
: refValues
) {
377 if (refValue
.equals(newValue
))
380 PropertyDiff pDiff
= new PropertyDiff(
381 PropertyDiff
.ADDED
, relPath
, null, newValue
);
382 diffs
.put(relPath
+ "_" + i
++, pDiff
);
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
);
399 pit
= observed
.getProperties();
401 while (pit
.hasNext()) {
402 Property p
= pit
.nextProperty();
403 String name
= p
.getName();
404 // if (name.startsWith("jcr:"))
406 if (!reference
.hasProperty(name
)) {
407 String relPath
= propertyRelPath(baseRelPath
, name
);
408 if (p
.isMultiple()) {
409 Value
[] newValues
= observed
.getProperty(name
)
412 for (Value newValue
: newValues
) {
413 PropertyDiff pDiff
= new PropertyDiff(
414 PropertyDiff
.ADDED
, relPath
, null, newValue
);
415 diffs
.put(relPath
+ "_" + i
++, pDiff
);
418 PropertyDiff pDiff
= new PropertyDiff(
419 PropertyDiff
.ADDED
, relPath
, null, p
.getValue());
420 diffs
.put(relPath
, pDiff
);
424 } catch (RepositoryException e
) {
425 throw new AkbException("Cannot diff " + reference
+ " and "
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)
436 return baseRelPath
+ '/' + propertyName
;
439 /** prevent instantiation by others */
440 private AkbJcrUtils() {