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
.QueryObjectModelFactory
;
24 import javax
.jcr
.query
.qom
.Selector
;
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
;
31 /** Some static utils methods that might be factorized in a near future */
32 public class AkbJcrUtils
{
34 // /////////////////////////
37 * Return the type of alias that must be used given current item type
39 public static String
getAliasTypeForNode(Node itemTemplate
) {
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
;
47 throw new AkbException("No connector type define for node "
49 } catch (RepositoryException re
) {
50 throw new AkbException("Unable to login", re
);
55 * Return defined alias in the current environment given current item type
57 public static List
<Node
> getDefinedAliasForNode(Node itemTemplate
) {
59 Session session
= itemTemplate
.getSession();
60 QueryManager queryManager
= session
.getWorkspace()
62 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
63 String nodeType
= getAliasTypeForNode(itemTemplate
);
65 Selector source
= factory
.selector(nodeType
, nodeType
);
66 String basePath
= getCurrentEnvBasePath(itemTemplate
);
67 Constraint defaultC
= factory
.descendantNode(
68 source
.getSelectorName(), basePath
);
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();
82 NodeIterator ni
= result
.getNodes();
84 return JcrUtils
.nodeIteratorToList(ni
);
85 } catch (RepositoryException e
) {
86 throw new AkbException("Unable to list connector", e
);
91 * Return the current env base path
93 public static String
getCurrentEnvBasePath(Node akbNode
) {
95 if (akbNode
.getDepth() == 0)
96 // no base path for root node
99 Node parNode
= akbNode
.getParent();
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
110 parNode
= parNode
.getParent();
113 } catch (RepositoryException re
) {
114 throw new AkbException("Unable to login", re
);
118 // //////////////////////////////////
119 // METHODS THAT CAN BE FACTORIZED
121 * Call {@link Repository#login()} without exceptions (useful in super
122 * constructors and dependency injection).
124 public static Session
login(Repository repository
) {
126 return repository
.login();
127 } catch (RepositoryException re
) {
128 throw new AkbException("Unable to login", re
);
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>
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
));
149 * Check if a string is null or an empty string (a string with only spaces
150 * is considered as empty
152 public static boolean isEmptyString(String stringToTest
) {
153 return stringToTest
== null || "".equals(stringToTest
.trim());
157 * Check if a string is null or an empty string (a string with only spaces
158 * is considered as empty
160 public static boolean checkNotEmptyString(String string
) {
161 return string
!= null && !"".equals(string
.trim());
165 * Wraps the versionMananger.isCheckedOut(path) method to adapt it to the
166 * current check in / check out policy.
168 * TODO : add management of check out by others.
170 public static boolean isNodeCheckedOut(Node node
) {
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",
181 * For the time being, same as isNodeCheckedOut(Node node).
183 * TODO : add management of check out by others.
185 public static boolean isNodeCheckedOutByMe(Node node
) {
186 return isNodeCheckedOut(node
);
190 * Wraps the versionMananger.checkedOut(path) method to adapt it to the
191 * current check in / check out policy.
193 * TODO : add management of check out by others.
195 public static void checkout(Node node
) {
197 node
.getSession().getWorkspace().getVersionManager()
198 .checkout(node
.getPath());
199 } catch (RepositoryException re
) {
200 throw new AkbException("Unable to check out Node", re
);
205 * Wraps the versionMananger.checkedIn(path) method to adapt it to the
206 * current check in / check out policy.
208 * It also checked if the current entity has to be moved or not. TODO : add
209 * management of check out by others.
211 public static void saveAndCheckin(Node node
) {
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
);
223 * Wraps the versionMananger.checkedIn(path) method to adapt it to the
224 * current check in / check out policy.
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
230 public static void cancelAndCheckin(Node node
) {
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
);
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.
248 public static String
get(Node node
, String propertyName
) {
250 if (!node
.hasProperty(propertyName
))
253 return node
.getProperty(propertyName
).getString();
254 } catch (RepositoryException e
) {
255 throw new AkbException("Cannot get property " + propertyName
261 * Concisely get the value of a property or null if this node doesn't have
264 public static Boolean
getBooleanValue(Node node
, String propertyName
) {
266 if (!node
.hasProperty(propertyName
))
269 return node
.getProperty(propertyName
).getBoolean();
270 } catch (RepositoryException e
) {
271 throw new AkbException("Cannot get boolean property "
272 + propertyName
+ " of " + node
, e
);
277 * Concisely get the identifier of a node in Ui listener for instance
279 public static String
getIdentifierQuietly(Node node
) {
281 return node
.getIdentifier();
282 } catch (RepositoryException e
) {
283 throw new AkbException("Cannot get identifier for node " + node
, e
);
287 public static Map
<String
, PropertyDiff
> diffProperties(Node reference
,
289 Map
<String
, PropertyDiff
> diffs
= new TreeMap
<String
, PropertyDiff
>();
290 diffPropertiesLevel(diffs
, null, reference
, observed
);
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.
302 static void diffPropertiesLevel(Map
<String
, PropertyDiff
> diffs
,
303 String baseRelPath
, Node reference
, Node observed
) {
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:"))
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
);
319 if (p
.isMultiple()) {
322 Value
[] refValues
= p
.getValues();
323 Value
[] newValues
= observed
.getProperty(name
)
325 String relPath
= propertyRelPath(baseRelPath
, name
);
326 refValues
: for (Value refValue
: refValues
) {
327 for (Value newValue
: newValues
) {
328 if (refValue
.equals(newValue
))
331 PropertyDiff pDiff
= new PropertyDiff(
332 PropertyDiff
.REMOVED
, relPath
, refValue
,
334 diffs
.put(relPath
+ "_" + i
++, pDiff
);
337 newValues
: for (Value newValue
: newValues
) {
338 for (Value refValue
: refValues
) {
339 if (refValue
.equals(newValue
))
342 PropertyDiff pDiff
= new PropertyDiff(
343 PropertyDiff
.ADDED
, relPath
, null, newValue
);
344 diffs
.put(relPath
+ "_" + i
++, pDiff
);
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
);
361 pit
= observed
.getProperties();
363 while (pit
.hasNext()) {
364 Property p
= pit
.nextProperty();
365 String name
= p
.getName();
366 // if (name.startsWith("jcr:"))
368 if (!reference
.hasProperty(name
)) {
369 String relPath
= propertyRelPath(baseRelPath
, name
);
370 if (p
.isMultiple()) {
371 Value
[] newValues
= observed
.getProperty(name
)
374 for (Value newValue
: newValues
) {
375 PropertyDiff pDiff
= new PropertyDiff(
376 PropertyDiff
.ADDED
, relPath
, null, newValue
);
377 diffs
.put(relPath
+ "_" + i
++, pDiff
);
380 PropertyDiff pDiff
= new PropertyDiff(
381 PropertyDiff
.ADDED
, relPath
, null, p
.getValue());
382 diffs
.put(relPath
, pDiff
);
386 } catch (RepositoryException e
) {
387 throw new AkbException("Cannot diff " + reference
+ " and "
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)
398 return baseRelPath
+ '/' + propertyName
;
401 /** prevent instantiation by others */
402 private AkbJcrUtils() {