1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package no.sintef.xmldb;
22
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.StringTokenizer;
27
28 import javax.naming.Context;
29 import javax.naming.InitialContext;
30 import javax.naming.NamingException;
31
32 import org.apache.log4j.Logger;
33 import org.exist.jboss.XmlDbService;
34 import org.xmldb.api.base.Collection;
35 import org.xmldb.api.base.Resource;
36 import org.xmldb.api.base.ResourceSet;
37 import org.xmldb.api.base.Service;
38 import org.xmldb.api.base.XMLDBException;
39 import org.xmldb.api.modules.CollectionManagementService;
40 import org.xmldb.api.modules.XMLResource;
41 import org.xmldb.api.modules.XPathQueryService;
42
43 /***
44 * XML:DB database handler.
45 *
46 * @author Fredrik Vraalsen
47 */
48 public final class XmlDbHandler {
49
50 private static final Logger LOGGER = Logger.getLogger(XmlDbHandler.class);
51 private static XmlDbHandler handler = null;
52
53 private XmlDbService xmlDbService = null;
54
55 /***
56 * Singleton class, use getHandler() method.
57 */
58 private XmlDbHandler() {
59 try {
60 Context ctx = new InitialContext();
61 xmlDbService = (XmlDbService) ctx.lookup(XmlDbService.class.getName());
62 } catch (NamingException e) {
63 LOGGER.fatal("Unable to lookup XmlDbService", e);
64 }
65 }
66
67 /***
68 * Gets the XmlDbHandler instance.
69 *
70 * @return the XmlDbHandler
71 */
72 public static synchronized XmlDbHandler getHandler() {
73 if (handler == null) {
74 try {
75 handler = new XmlDbHandler();
76 } catch (Throwable t) {
77 LOGGER.fatal("Unable to create XmlDbHandler", t);
78 }
79 }
80 return handler;
81 }
82
83 /***
84 * Get first XMLResource in collection matching specified query.
85 *
86 * TODO includes subcollections?
87 *
88 * @param parent
89 * full path of parent collection, using '/' as path separator
90 * @param query
91 * XPath query string
92 * @param namespaceMappings
93 * prefix -> namespace URI mappings
94 * @return first XMLResource matching query, or null if none is found or
95 * error occurs
96 */
97 public XMLResource getSingleXmlResource(String parent, String query, Map namespaceMappings) {
98 Collection c = null;
99 try {
100 c = getCollection(parent);
101 ResourceSet set = getResourceSet(c, query, namespaceMappings);
102 if (set == null || set.getSize() == 0) {
103 return null;
104 }
105
106 Resource r = set.getResource(0);
107 if (r instanceof XMLResource) {
108 return (XMLResource) r;
109 }
110 } catch (XMLDBException e) {
111 LOGGER.error("Error while getting XML resource with query " + query
112 + " on collection " + parent, e);
113 } finally {
114 if (c != null) {
115 try {
116 c.close();
117 } catch (XMLDBException e) {
118 LOGGER.warn("Could not close collection " + c, e);
119 }
120 }
121 }
122 return null;
123 }
124
125 /***
126 * Get XMLResources in collection matching specified query. Resources which
127 * could not be loaded from database because of errors are not included in
128 * result.
129 *
130 * TODO includes subcollections?
131 *
132 * @param parent
133 * full path of parent collection, using '/' as path separator
134 * @param query
135 * XPath query
136 * @param namespaceMappings
137 * prefix -> namespace URI mappings
138 * @return array of XMLResources matching query, possibly empty (never null)
139 */
140 public XMLResource[] getXmlResources(String parent, String query, Map namespaceMappings) {
141 Collection c = null;
142 try {
143 c = getCollection(parent);
144 ResourceSet set = getResourceSet(c, query, namespaceMappings);
145 if (set == null) {
146 return new XMLResource[0];
147 }
148 ArrayList resources = new ArrayList();
149 for (int i = 0; i < set.getSize(); i++) {
150 try {
151 Resource r = set.getResource(i);
152 if (r instanceof XMLResource) {
153 resources.add(r);
154 }
155 } catch (XMLDBException e) {
156 LOGGER.error("Error while getting XML resource with query " + query
157 + " on collection " + parent, e);
158 }
159 }
160 return (XMLResource[]) resources.toArray(new XMLResource[0]);
161 } catch (XMLDBException e) {
162 LOGGER.error("Error while getting XML resources with query " + query
163 + " on collection " + parent, e);
164 return new XMLResource[0];
165 } finally {
166 if (c != null) {
167 try {
168 c.close();
169 } catch (XMLDBException e) {
170 LOGGER.warn("Could not close collection " + c, e);
171 }
172 }
173 }
174 }
175
176 /***
177 * Create XMLResource in specified collection.
178 *
179 * @param parent
180 * full path of parent collection to create resource in, using
181 * '/' as path separator
182 * @return the created XMLResource, or null if error occured
183 */
184 public XMLResource createXmlResource(String parent) {
185 if (parent == null) {
186 return null;
187 }
188 Collection c = getCollection(parent);
189 if (c == null) {
190 c = createCollection(parent);
191 }
192 if (c == null) {
193 return null;
194 }
195 try {
196 return (XMLResource) c.createResource(null, XMLResource.RESOURCE_TYPE);
197 } catch (XMLDBException e) {
198 LOGGER.error("Could not create XMLResource in collection " + parent, e);
199 return null;
200 } finally {
201 try {
202 c.close();
203 } catch (XMLDBException e) {
204 LOGGER.warn("Could not close collection " + c, e);
205 }
206 }
207 }
208
209 /***
210 * Delete XMLResource.
211 *
212 * @param resource
213 * resource to delete
214 * @throws XMLDBException
215 * if resource cannot be deleted from database
216 */
217 public void deleteResource(XMLResource resource) throws XMLDBException {
218 if (resource == null) {
219 return;
220 }
221 Collection c = null;
222 try {
223 c = resource.getParentCollection();
224 c.removeResource(resource);
225 } catch (XMLDBException e) {
226 LOGGER.error("Could not remove XMLResource " + resource + " from collection " + c, e);
227 throw e;
228 } finally {
229 if (c != null) {
230 try {
231 c.close();
232 } catch (XMLDBException e) {
233 LOGGER.warn("Could not close collection " + c, e);
234 }
235 }
236 }
237 }
238
239 /***
240 * Store an XMLResource in the database.
241 *
242 * @param resource
243 * the XMLResource to store
244 * @throws XMLDBException
245 * if error occurs when storing resource in database
246 */
247 public void storeResource(XMLResource resource) throws XMLDBException {
248 if (resource == null) {
249 return;
250 }
251 Collection c = null;
252 try {
253 c = resource.getParentCollection();
254 c.storeResource(resource);
255 } catch (XMLDBException e) {
256 LOGGER.error("Could not store XMLResource " + resource.getDocumentId()
257 + " in collection " + c.getName(), e);
258 throw e;
259 } finally {
260 if (c != null) {
261 try {
262 c.close();
263 } catch (XMLDBException e) {
264 LOGGER.warn("Could not close collection " + c, e);
265 }
266 }
267 }
268 }
269
270 /***
271 * Create a collection in the database.
272 *
273 * @param path
274 * full path specifying location and name of collection, using
275 * '/' as path separator
276 * @return the XML:DB Collection, or null if error occured
277 */
278 private Collection createCollection(String path) {
279 LOGGER.debug("Create collection " + path);
280 if (path == null) {
281 return null;
282 }
283 int pos = path.lastIndexOf('/');
284 String parentName = pos == -1 ? "" : path.substring(0, pos);
285 String name = pos == -1 ? path : path.substring(pos + 1);
286 Collection parentCollection = null;
287 try {
288 parentCollection = getCollection(parentName);
289 if (parentCollection == null && !("".equals(parentName))) {
290 parentCollection = createCollection(parentName);
291 }
292 if (parentCollection == null) {
293 LOGGER.error("Could not create parent collection " + parentName);
294 return null;
295 }
296 CollectionManagementService service =
297 (CollectionManagementService) parentCollection.getService(
298 "CollectionManagementService", "1.0");
299 return service.createCollection(name);
300 } catch (XMLDBException e) {
301 LOGGER.error("Could not create Collection " + path, e);
302 return null;
303 } finally {
304 try {
305 parentCollection.close();
306 } catch (XMLDBException e) {
307 LOGGER.warn("Could not close collection " + parentCollection, e);
308 }
309 }
310 }
311
312 /***
313 * Get the set of XMLResources matching the query.
314 *
315 * @param parent
316 * the parent collection
317 * @param query
318 * XPath query string
319 * @param namespaceMappings
320 * prefix -> namespace URI mappings
321 * @return ResourceSet containing XMLResources matching query
322 */
323 private ResourceSet getResourceSet(Collection parent, String query, Map namespaceMappings) {
324 LOGGER.debug("getResourceSet(" + parent + ", " + query + ")");
325 if (query == null || parent == null) {
326 return null;
327 }
328
329 XPathQueryService queryService = getXPathQueryService(parent);
330 try {
331
332 if (namespaceMappings != null) {
333 for (Iterator i = namespaceMappings.keySet().iterator(); i.hasNext();) {
334 Object name = i.next();
335 Object uri = namespaceMappings.get(name);
336 if ((name instanceof String) && (uri instanceof String)) {
337 queryService.setNamespace((String) name, (String) uri);
338 }
339 }
340 }
341 return queryService.query(query);
342 } catch (XMLDBException e) {
343 LOGGER.error("Unable to perform XPath query " + query + " on collection " + parent, e);
344 return null;
345 } finally {
346 try {
347 parent.close();
348 } catch (XMLDBException e) {
349 LOGGER.warn("Unable to close collection " + parent, e);
350 }
351 }
352 }
353
354 /***
355 * Get the specified Collection from the database.
356 *
357 * @param path
358 * full path specifying location and name of collection, using
359 * '/' as path separator
360 * @return the Collection, or null if error occurs
361 */
362 private Collection getCollection(String path) {
363 try {
364 if (path == null) {
365 return null;
366 }
367 Collection result = xmlDbService.getBaseCollection();
368 StringTokenizer tk = new StringTokenizer(path, "/");
369 while (result != null && tk.hasMoreTokens()) {
370 String name = tk.nextToken();
371 if (name == null || name.equals("")) {
372 continue;
373 }
374 result = result.getChildCollection(name);
375 }
376 return result;
377 } catch (XMLDBException e) {
378 LOGGER.error("Error while looking up collection " + path, e);
379 return null;
380 }
381 }
382
383 /***
384 * Get XPathQueryService for collection.
385 *
386 * @param collection
387 * the collection
388 * @return the XPathQueryService, or null if error occurs
389 */
390 private static XPathQueryService getXPathQueryService(Collection collection) {
391 if (collection == null) {
392 return null;
393 }
394 Service s = null;
395 try {
396 s = collection.getService("XPathQueryService", "1.0");
397 } catch (XMLDBException e) {
398 LOGGER.error("Error while getting XPathQueryService for collection " + collection, e);
399 }
400 if (s instanceof XPathQueryService) {
401 return (XPathQueryService) s;
402 } else {
403 LOGGER.error("Unable to get XPathQueryService for collection " + collection + ", got " + s);
404 return null;
405 }
406 }
407
408
409 }