1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package no.sintef.assetrepository.dao;
22
23 import java.io.BufferedOutputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.UnsupportedEncodingException;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.Method;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34
35 import javax.ejb.CreateException;
36 import javax.ejb.DuplicateKeyException;
37 import javax.ejb.EJBException;
38 import javax.ejb.EntityBean;
39 import javax.ejb.FinderException;
40 import javax.ejb.NoSuchEntityException;
41 import javax.ejb.ObjectNotFoundException;
42 import javax.ejb.RemoveException;
43 import javax.xml.bind.JAXBContext;
44 import javax.xml.bind.JAXBException;
45 import javax.xml.bind.Marshaller;
46 import javax.xml.bind.Unmarshaller;
47 import javax.xml.bind.UnmarshallerHandler;
48 import javax.xml.bind.ValidationEvent;
49 import javax.xml.bind.ValidationEventHandler;
50 import javax.xml.bind.ValidationEventLocator;
51 import javax.xml.bind.Validator;
52
53 import no.sintef.assetrepository.jaxb.IOid;
54 import no.sintef.assetrepository.jaxb.impl.IOidImpl;
55 import no.sintef.xml.XmlHelper;
56 import no.sintef.xmldb.XmlDbHandler;
57
58 import org.apache.log4j.Logger;
59 import org.xml.sax.Attributes;
60 import org.xml.sax.ContentHandler;
61 import org.xml.sax.Locator;
62 import org.xml.sax.SAXException;
63 import org.xml.sax.helpers.AttributesImpl;
64 import org.xmldb.api.base.XMLDBException;
65 import org.xmldb.api.modules.XMLResource;
66
67 /***
68 * @author fvr
69 *
70 * To change the template for this generated type comment go to
71 * Window - Preferences - Java - Code Generation - Code and Comments
72 */
73 public abstract class AbstractDAO {
74
75 private static final Logger LOGGER = Logger.getLogger(AbstractDAO.class);
76 private static final String AR_NAMESPACE_NAME = "ar";
77 private static final String AR_NAMESPACE_URI = "http://www.sintef.no/2004/schemas/asset-repository-1_0.xsd";
78 private static final Map NAMESPACE_MAPPING = new HashMap();
79
80 private static final String EXIST_NAMESPACE_URI = "http://exist.sourceforge.net/NS/exist";
81
82 private XmlDbHandler dbHandler = null;
83 private Marshaller marshaller = null;
84 private Unmarshaller unmarshaller = null;
85 private Validator validator = null;
86 private String collection;
87
88
89 static {
90 NAMESPACE_MAPPING.put(AR_NAMESPACE_NAME, AR_NAMESPACE_URI);
91 }
92
93 /***
94 *
95 */
96 public AbstractDAO(String collection) {
97 super();
98 this.collection = collection;
99 }
100
101
102
103
104 public void init() {
105 try {
106 JAXBContext jc = JAXBContext.newInstance("no.sintef.assetrepository.jaxb");
107 marshaller = jc.createMarshaller();
108 marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
109 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 unmarshaller = jc.createUnmarshaller();
126 validator = jc.createValidator();
127
128
129 unmarshaller.setEventHandler(new ValidationEventHandler() {
130 public boolean handleEvent(ValidationEvent ev) {
131 LOGGER.error("JAXB validation event: " + ev.getMessage());
132 ValidationEventLocator locator = ev.getLocator();
133 LOGGER.error("Line: " + locator.getLineNumber() + ", Column: " + locator.getColumnNumber() + ", Offset: " + locator.getOffset());
134
135
136
137
138
139
140
141
142
143
144
145
146 return true;
147 }
148 });
149 unmarshaller.setValidating(true);
150
151 dbHandler = XmlDbHandler.getHandler();
152 } catch (JAXBException e) {
153
154 e.printStackTrace();
155 }
156 }
157
158 /***
159 *
160 * @param pk
161 * @param ejb
162 * @throws EJBException
163 */
164 protected void load(Object pk, EntityBean ejb) throws EJBException {
165 if (LOGGER.isDebugEnabled()) {
166 LOGGER.debug("Loading entity (" + (ejb != null ? ejb.getClass() : null) + ") with primary key " + pk);
167 }
168 XMLResource resource = null;
169 try {
170 if (ejb == null)
171 throw new EJBException("Entity bean instance is null");
172 resource = getXMLResource(pk);
173 Object jaxbObject = unmarshalResource(resource);
174 setJaxbObject(ejb, jaxbObject);
175 } catch (EJBException e) {
176 throw e;
177 } catch (Throwable t) {
178 LOGGER.error(t);
179 throw new EJBException("Error while loading Entity bean: " + t.getMessage());
180 }
181 }
182
183 /***
184 *
185 * @param ejb
186 * @throws EJBException
187 */
188 protected void store(EntityBean ejb) throws EJBException {
189 String id = getId(ejb);
190 if (LOGGER.isDebugEnabled()) {
191 LOGGER.debug("Storing entity (" + (ejb != null ? ejb.getClass() : null) + ") with id " + id);
192 }
193 try {
194 XMLResource resource = getXMLResource(ejb);
195 Object jaxbObject = createJaxbObject(ejb);
196
197 if (LOGGER.isDebugEnabled()) {
198 ByteArrayOutputStream baos = new ByteArrayOutputStream();
199 marshaller.marshal(jaxbObject, new BufferedOutputStream(baos));
200 LOGGER.debug("marshalling: " + new String(baos.toByteArray(), "UTF-8"));
201 }
202
203 validator.setEventHandler(new ValidationEventHandler() {
204 public boolean handleEvent(ValidationEvent ev) {
205 LOGGER.error("JAXB validation error: " + ev.getMessage());
206 return true;
207 }
208 });
209 validator.validate(jaxbObject);
210
211 storeResource(jaxbObject, resource);
212 } catch (XMLDBException e) {
213 throw new EJBException("Unable to store entity bean because of database error: " + e.getMessage());
214 } catch (EJBException e) {
215 throw e;
216 } catch (Throwable t) {
217 LOGGER.error(t);
218 throw new EJBException("Error while storing Entity bean " + id + ": " + t.getMessage());
219 }
220 }
221
222 /***
223 * @param jaxbObject
224 * @param resource
225 * @throws XMLDBException
226 * @throws SAXException
227 * @throws JAXBException
228 */
229 private void storeResource(Object jaxbObject, XMLResource resource) throws XMLDBException, SAXException, JAXBException {
230 ContentHandler contentHandler = resource.setContentAsSAX();
231
232 marshaller.marshal(jaxbObject, contentHandler);
233
234 dbHandler.storeResource(resource);
235 }
236
237 /***
238 *
239 * @param pk
240 * @throws RemoveException
241 * @throws EJBException
242 */
243 protected void remove(Object pk) throws RemoveException, EJBException {
244 if (LOGGER.isDebugEnabled()) {
245 LOGGER.debug("Removing entity with primary key " + pk);
246 }
247 try {
248 XMLResource resource = getXMLResource(pk);
249 dbHandler.deleteResource(resource);
250 } catch (XMLDBException e) {
251 throw new RemoveException("Unable to remove entity with primary key " + pk + ": " + e.getMessage());
252 } catch (EJBException e) {
253 throw e;
254 } catch (Throwable t) {
255 LOGGER.error(t);
256 throw new EJBException("Error while removing Entity bean " + pk + ": " + t.getMessage());
257 }
258 }
259
260 /***
261 *
262 * @param ejb
263 * @return
264 * @throws CreateException
265 * @throws EJBException
266 */
267 protected Object create(EntityBean ejb) throws CreateException, EJBException {
268 if (LOGGER.isDebugEnabled()) {
269 LOGGER.debug("Creating entity (" + (ejb != null ? ejb.getClass() : null) + ") with id " + getId(ejb));
270 }
271 try {
272 if (ejb == null)
273 throw new EJBException("Entity bean is null");
274 String id = getId(ejb);
275 if (id == null)
276 throw new CreateException("Entity id is null");
277 Object pk = getPrimaryKey(id);
278 try {
279 if (findByPrimaryKey(pk) != null) {
280 throw new DuplicateKeyException("Duplicate primary key: " + pk);
281 }
282 } catch (FinderException e) {
283
284 }
285
286 XMLResource resource = dbHandler.createXmlResource(collection);
287
288 Object jaxbObject = createJaxbObject(ejb);
289 storeResource(jaxbObject, resource);
290 return pk;
291 } catch (DuplicateKeyException e) {
292 throw e;
293 } catch (EJBException e) {
294 throw e;
295 } catch (XMLDBException e) {
296 throw new CreateException("Unable to create entity resource due to database error: " + e.getMessage());
297 } catch (Throwable t) {
298 LOGGER.error(t);
299 throw new CreateException("Error while creating Entity bean: " + t.getMessage());
300 }
301 }
302
303 /***
304 * @param pk
305 * @return
306 */
307 protected Object findByPrimaryKey(Object pk) throws FinderException {
308 if (LOGGER.isDebugEnabled()) {
309 LOGGER.debug("findByPrimaryKey(" + pk + ")");
310 }
311 try {
312 XMLResource resource = getXMLResource(pk);
313 return pk;
314 } catch (NoSuchEntityException e) {
315 throw new ObjectNotFoundException("Could not find entity resource with primary key " + pk);
316 } catch (Throwable t) {
317 LOGGER.error(t);
318 throw new ObjectNotFoundException("Could not find entity resource with primary key " + pk);
319 }
320 }
321
322 /***
323 *
324 * @param query
325 * @return
326 */
327 protected Collection getPrimaryKeys(String query) {
328 if (LOGGER.isDebugEnabled()) {
329 LOGGER.debug("getPrimaryKeys(" + query + ")");
330 }
331 XMLResource resource = null;
332 try {
333 XMLResource[] resources = dbHandler.getXmlResources(collection, query, NAMESPACE_MAPPING);
334 ArrayList result = new ArrayList(resources.length);
335 for (int i = 0; i < resources.length; i++) {
336 resource = resources[i];
337 Object jaxbObject = unmarshalResource(resource);
338 String id = getId(jaxbObject);
339 if (id != null) {
340 result.add(getPrimaryKey(id));
341 }
342 }
343 return result;
344 } catch (Throwable t) {
345 LOGGER.error(t);
346 return new ArrayList();
347 }
348 }
349
350 protected void pksToOids(Collection pks, List oids, Class pkClass) {
351 if (pks == null) {
352 LOGGER.warn("pksToOids: pks is null");
353 return;
354 }
355 if (oids == null) {
356 LOGGER.error("pksToOids: oids is null");
357 return;
358 }
359 try {
360 Method getIdMethod = pkClass.getMethod("getId", null);
361 for (Iterator i = pks.iterator(); i.hasNext();) {
362 Object pk = i.next();
363 if (! (pkClass.isInstance(pk))) {
364 LOGGER.warn("Expected " + pkClass.getName() + "primary key, got " + (pk != null ? pk.getClass() : null));
365 continue;
366 }
367 IOid oid = new IOidImpl();
368 String id = (String) getIdMethod.invoke(pk, null);
369 oid.setOid(id);
370 oids.add(oid);
371 }
372 } catch (Throwable t) {
373 LOGGER.error(t);
374 }
375 }
376
377 protected Collection oidsToPks(List oids, Class pkClass) {
378 Collection pks = new ArrayList();
379 if (oids == null) {
380 LOGGER.warn("oidsToPks: oids is null");
381 return pks;
382 }
383 try {
384 Class[] clazz = { String.class };
385 Constructor constructor = pkClass.getConstructor(clazz);
386 for (Iterator i = oids.iterator(); i.hasNext();) {
387 Object o = i.next();
388 if (! (o instanceof IOid)) {
389 LOGGER.warn("Expected oid, got " + (o != null ? o.getClass() : null));
390 continue;
391 }
392 IOid oid = (IOid) o;
393 Object[] args = { oid.getOid() };
394 Object pk = constructor.newInstance(args);
395 pks.add(pk);
396 }
397 } catch (Throwable t) {
398 LOGGER.error(t);
399 }
400 return pks;
401 }
402
403 /***
404 * @param pk
405 * @return
406 */
407 private XMLResource getXMLResource(Object pk) throws NoSuchEntityException {
408 String id = getId(pk);
409 if (id == null) {
410 throw new NoSuchEntityException("Primary key is null");
411 }
412 String query = getSingleResourceQuery(id);
413 XMLResource[] resources = dbHandler.getXmlResources(collection, query, NAMESPACE_MAPPING);
414 if (resources == null || resources.length == 0 || resources[0] == null) {
415 throw new NoSuchEntityException("No entity with primary key " + id + " found for query " + query);
416 }
417 if (resources.length > 1) {
418 LOGGER.error("Primary key " + id + " is not unique");
419 }
420 return resources[0];
421 }
422
423 /***
424 * @param resource
425 * @return
426 */
427 private Object unmarshalResource(XMLResource resource) {
428 try {
429
430
431 if (LOGGER.isDebugEnabled()) {
432 byte[] bytes = XmlHelper.xmlToUtf8(resource.getContentAsDOM(), true);
433 try {
434 LOGGER.debug("unmarshalling: " + new String(bytes, "UTF-8"));
435 } catch (UnsupportedEncodingException e) {
436
437 e.printStackTrace();
438 }
439 }
440
441
442
443 final UnmarshallerHandler contentHandler = unmarshaller.getUnmarshallerHandler();
444 ContentHandler filter = new ContentHandler() {
445 public void setDocumentLocator(Locator locator) {
446 contentHandler.setDocumentLocator(locator);
447 }
448 public void startDocument() throws SAXException {
449 contentHandler.startDocument();
450 }
451 public void endDocument() throws SAXException {
452 contentHandler.endDocument();
453 }
454 public void startPrefixMapping(String prefix, String uri) throws SAXException {
455 contentHandler.startPrefixMapping(prefix, uri);
456 }
457 public void endPrefixMapping(String prefix) throws SAXException {
458 contentHandler.endPrefixMapping(prefix);
459 }
460 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
461 AttributesImpl newAtts = new AttributesImpl();
462 for (int i = 0; i < atts.getLength(); i++) {
463 String uri = atts.getURI(i);
464 if (!EXIST_NAMESPACE_URI.equals(uri)) {
465 newAtts.addAttribute(uri,
466 atts.getLocalName(i),
467 atts.getQName(i),
468 atts.getType(i),
469 atts.getValue(i));
470 }
471 }
472 contentHandler.startElement(namespaceURI, localName, qName, newAtts);
473 }
474 public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
475 contentHandler.endElement(namespaceURI, localName, qName);
476 }
477 public void characters(char[] ch, int start, int length) throws SAXException {
478 contentHandler.characters(ch, start, length);
479 }
480 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
481 contentHandler.ignorableWhitespace(ch, start, length);
482 }
483 public void processingInstruction(String target, String data) throws SAXException {
484 contentHandler.processingInstruction(target, data);
485 }
486 public void skippedEntity(String name) throws SAXException {
487 contentHandler.skippedEntity(name);
488 }
489 };
490
491
492 resource.getContentAsSAX(filter);
493 return contentHandler.getResult();
494 } catch (XMLDBException e) {
495 throw new EJBException("Database error while loading XML resource: " + e.getMessage(), e);
496 } catch (Throwable e) {
497 if (LOGGER.isDebugEnabled()) {
498
499
500
501
502
503
504
505 try {
506 LOGGER.debug("Error unmarshalling XML:");
507 LOGGER.debug(resource.getContent());
508 } catch (XMLDBException xe) {
509 LOGGER.debug(xe);
510 }
511 }
512 if (e instanceof Exception) {
513 throw new EJBException("Error while parsing XML content: " + e.getMessage(), (Exception)e);
514 } else {
515 LOGGER.error(e);
516 throw new EJBException("Error while parsing XML content: " + e.getMessage());
517 }
518 }
519 }
520
521 /***
522 * @param o The object containing the primary key.
523 * May be an Entity bean, primary key or JAXB object.
524 * @return
525 */
526 protected abstract String getId(Object o);
527
528 /***
529 * @param id The primary key ID, <strong>never</strong> <code>null</code>.
530 * @return
531 */
532 protected abstract Object getPrimaryKey(String id);
533
534 /***
535 * @param ejb
536 * @return
537 */
538 protected abstract Object createJaxbObject(EntityBean ejb);
539
540 /***
541 * @param ejb
542 * @param jaxbObject
543 */
544 protected abstract void setJaxbObject(EntityBean ejb, Object jaxbObject);
545
546 /***
547 * @param id
548 * @return
549 */
550 protected abstract String getSingleResourceQuery(String id);
551
552 /***
553 * @param id
554 * @return
555 */
556 protected static final IOid createIOid(String id) {
557 IOid oid = new IOidImpl();
558 oid.setOid(id);
559 return oid;
560 }
561
562 }
563
564