2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org
.argeo
.jcr
;
19 import java
.text
.DateFormat
;
20 import java
.text
.ParseException
;
21 import java
.util
.Calendar
;
22 import java
.util
.Date
;
23 import java
.util
.GregorianCalendar
;
24 import java
.util
.StringTokenizer
;
26 import javax
.jcr
.NamespaceRegistry
;
27 import javax
.jcr
.Node
;
28 import javax
.jcr
.NodeIterator
;
29 import javax
.jcr
.Property
;
30 import javax
.jcr
.PropertyIterator
;
31 import javax
.jcr
.RepositoryException
;
32 import javax
.jcr
.Session
;
33 import javax
.jcr
.Value
;
34 import javax
.jcr
.query
.Query
;
35 import javax
.jcr
.query
.QueryResult
;
37 import org
.apache
.commons
.logging
.Log
;
38 import org
.apache
.commons
.logging
.LogFactory
;
39 import org
.argeo
.ArgeoException
;
41 /** Utility methods to simplify common JCR operations. */
42 public class JcrUtils
{
43 private final static Log log
= LogFactory
.getLog(JcrUtils
.class);
46 * Queries one single node.
48 * @return one single node or null if none was found
49 * @throws ArgeoException
50 * if more than one node was found
52 public static Node
querySingleNode(Query query
) {
53 NodeIterator nodeIterator
;
55 QueryResult queryResult
= query
.execute();
56 nodeIterator
= queryResult
.getNodes();
57 } catch (RepositoryException e
) {
58 throw new ArgeoException("Cannot execute query " + query
, e
);
61 if (nodeIterator
.hasNext())
62 node
= nodeIterator
.nextNode();
66 if (nodeIterator
.hasNext())
67 throw new ArgeoException("Query returned more than one node.");
71 /** Removes forbidden characters from a path, replacing them with '_' */
72 public static String
removeForbiddenCharacters(String str
) {
73 return str
.replace('[', '_').replace(']', '_').replace('/', '_')
78 /** Retrieves the parent path of the provided path */
79 public static String
parentPath(String path
) {
81 throw new ArgeoException("Root path '/' has no parent path");
82 if (path
.charAt(0) != '/')
83 throw new ArgeoException("Path " + path
+ " must start with a '/'");
85 if (pathT
.charAt(pathT
.length() - 1) == '/')
86 pathT
= pathT
.substring(0, pathT
.length() - 2);
88 int index
= pathT
.lastIndexOf('/');
89 return pathT
.substring(0, index
);
92 /** The provided data as a path ('/' at the end, not the beginning) */
93 public static String
dateAsPath(Calendar cal
) {
94 return dateAsPath(cal
, false);
98 * The provided data as a path ('/' at the end, not the beginning)
103 * whether to add hour as well
105 public static String
dateAsPath(Calendar cal
, Boolean addHour
) {
106 StringBuffer buf
= new StringBuffer(14);
107 buf
.append('Y').append(cal
.get(Calendar
.YEAR
));// 5
109 int month
= cal
.get(Calendar
.MONTH
) + 1;
113 buf
.append(month
);// 3
115 int day
= cal
.get(Calendar
.DAY_OF_MONTH
);
118 buf
.append('D').append(day
);// 3
121 int hour
= cal
.get(Calendar
.HOUR_OF_DAY
);
124 buf
.append('H').append(hour
);// 3
127 return buf
.toString();
131 /** Converts in one call a string into a gregorian calendar. */
132 public static Calendar
parseCalendar(DateFormat dateFormat
, String value
) {
134 Date date
= dateFormat
.parse(value
);
135 Calendar calendar
= new GregorianCalendar();
136 calendar
.setTime(date
);
138 } catch (ParseException e
) {
139 throw new ArgeoException("Cannot parse " + value
140 + " with date format " + dateFormat
, e
);
145 /** Converts the FQDN of an host into a path (converts '.' into '/'). */
146 public static String
hostAsPath(String host
) {
147 // TODO : inverse order of the elements (to have org/argeo/test IO
149 return host
.replace('.', '/');
152 /** The last element of a path. */
153 public static String
lastPathElement(String path
) {
154 if (path
.charAt(path
.length() - 1) == '/')
155 throw new ArgeoException("Path " + path
+ " cannot end with '/'");
156 int index
= path
.lastIndexOf('/');
158 throw new ArgeoException("Cannot find last path element for "
160 return path
.substring(index
+ 1);
163 /** Creates the nodes making path, if they don't exist. */
164 public static Node
mkdirs(Session session
, String path
) {
165 return mkdirs(session
, path
, null, false);
168 /** Creates the nodes making path, if they don't exist. */
169 public static Node
mkdirs(Session session
, String path
, String type
,
170 Boolean versioning
) {
172 if (path
.equals('/'))
173 return session
.getRootNode();
175 if (session
.itemExists(path
)) {
176 Node node
= session
.getNode(path
);
179 && !type
.equals(node
.getPrimaryNodeType().getName()))
180 throw new ArgeoException("Node " + node
181 + " exists but is of type "
182 + node
.getPrimaryNodeType().getName()
183 + " not of type " + type
);
184 // TODO: check versioning
188 StringTokenizer st
= new StringTokenizer(path
, "/");
189 StringBuffer current
= new StringBuffer("/");
190 Node currentNode
= session
.getRootNode();
191 while (st
.hasMoreTokens()) {
192 String part
= st
.nextToken();
193 current
.append(part
).append('/');
194 if (!session
.itemExists(current
.toString())) {
196 currentNode
= currentNode
.addNode(part
, type
);
198 currentNode
= currentNode
.addNode(part
);
200 currentNode
.addMixin(ArgeoJcrConstants
.MIX_VERSIONABLE
);
201 if (log
.isTraceEnabled())
202 log
.debug("Added folder " + part
+ " as " + current
);
204 currentNode
= (Node
) session
.getItem(current
.toString());
209 } catch (RepositoryException e
) {
210 throw new ArgeoException("Cannot mkdirs " + path
, e
);
215 * Safe and repository implementation independent registration of a
218 public static void registerNamespaceSafely(Session session
, String prefix
,
221 registerNamespaceSafely(session
.getWorkspace()
222 .getNamespaceRegistry(), prefix
, uri
);
223 } catch (RepositoryException e
) {
224 throw new ArgeoException("Cannot find namespace registry", e
);
229 * Safe and repository implementation independent registration of a
232 public static void registerNamespaceSafely(NamespaceRegistry nr
,
233 String prefix
, String uri
) {
235 String
[] prefixes
= nr
.getPrefixes();
236 for (String pref
: prefixes
)
237 if (pref
.equals(prefix
)) {
238 String registeredUri
= nr
.getURI(pref
);
239 if (!registeredUri
.equals(uri
))
240 throw new ArgeoException("Prefix " + pref
241 + " already registered for URI "
243 + " which is different from provided URI "
248 nr
.registerNamespace(prefix
, uri
);
249 } catch (RepositoryException e
) {
250 throw new ArgeoException("Cannot register namespace " + uri
251 + " under prefix " + prefix
, e
);
255 /** Recursively outputs the contents of the given node. */
256 public static void debug(Node node
) {
258 // First output the node path
259 log
.debug(node
.getPath());
260 // Skip the virtual (and large!) jcr:system subtree
261 if (node
.getName().equals(ArgeoJcrConstants
.JCR_SYSTEM
)) {
265 // Then the children nodes (recursive)
266 NodeIterator it
= node
.getNodes();
267 while (it
.hasNext()) {
268 Node childNode
= it
.nextNode();
272 // Then output the properties
273 PropertyIterator properties
= node
.getProperties();
274 // log.debug("Property are : ");
276 while (properties
.hasNext()) {
277 Property property
= properties
.nextProperty();
278 if (property
.getDefinition().isMultiple()) {
279 // A multi-valued property, print all values
280 Value
[] values
= property
.getValues();
281 for (int i
= 0; i
< values
.length
; i
++) {
282 log
.debug(property
.getPath() + "="
283 + values
[i
].getString());
286 // A single-valued property
287 log
.debug(property
.getPath() + "=" + property
.getString());
290 } catch (Exception e
) {
291 log
.error("Could not debug " + node
, e
);