]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
a85e810f2a84c9a835a0df6ec2fa6fb1d83e1325
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17  */
18 package org.collectionspace.services.common.storage.jpa;
19
20 import java.util.Date;
21 import java.util.List;
22 import javax.persistence.EntityManager;
23 import javax.persistence.EntityManagerFactory;
24 import javax.persistence.Query;
25 import org.collectionspace.services.common.context.ServiceContext;
26 import org.collectionspace.services.common.document.BadRequestException;
27 import org.collectionspace.services.common.document.DocumentException;
28 import org.collectionspace.services.common.document.DocumentFilter;
29 import org.collectionspace.services.common.document.DocumentHandler;
30 import org.collectionspace.services.common.document.DocumentNotFoundException;
31 import org.collectionspace.services.common.document.DocumentHandler.Action;
32 import org.collectionspace.services.common.document.DocumentWrapper;
33 import org.collectionspace.services.common.document.DocumentWrapperImpl;
34 import org.collectionspace.services.common.document.JaxbUtils;
35 import org.collectionspace.services.common.storage.StorageClient;
36 import org.collectionspace.services.common.context.ServiceContextProperties;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * JpaStorageClient is used to perform CRUD operations on SQL storage using JPA.
42  * It uses @see DocumentHandler as IOHandler with the client.
43  * All the operations in this client are carried out under their own transactions.
44  * A call to any method would start and commit/rollback a transaction.
45  * 
46  * Assumption: each persistent entityReceived has the following 3 attributes
47 <xs:element name="createdAt" type="xs:dateTime">
48 <xs:annotation>
49 <xs:appinfo>
50 <hj:basic>
51 <orm:column name="created_at" nullable="false"/>
52 </hj:basic>
53 </xs:appinfo>
54 </xs:annotation>
55 </xs:element>
56 <xs:element name="updatedAt" type="xs:dateTime">
57 <xs:annotation>
58 <xs:appinfo>
59 <hj:basic>
60 <orm:column name="updated_at" />
61 </hj:basic>
62 </xs:appinfo>
63 </xs:annotation>
64 </xs:element>
65 </xs:sequence>
66 <xs:attribute name="csid" type="xs:string">
67 <xs:annotation>
68 <xs:appinfo>
69 <hj:csidReceived>
70 <orm:column name="csid" length="128" nullable="false"/>
71 </hj:csidReceived>
72 </xs:appinfo>
73 </xs:annotation>
74 </xs:attribute>
75  *
76  * $LastChangedRevision: $ $LastChangedDate: $
77  */
78 public class JpaStorageClientImpl implements StorageClient {
79
80     /** The logger. */
81     private final Logger logger = LoggerFactory.getLogger(JpaStorageClientImpl.class);
82
83     /**
84      * Instantiates a new jpa storage client.
85      */
86     public JpaStorageClientImpl() {
87     }
88
89     /* (non-Javadoc)
90      * @see org.collectionspace.services.common.storage.StorageClient#create(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
91      */
92     @Override
93     public String create(ServiceContext ctx,
94             DocumentHandler handler) throws BadRequestException,
95             DocumentException {
96
97         if (ctx == null) {
98             throw new IllegalArgumentException(
99                     "create: ctx is missing");
100         }
101         if (handler == null) {
102             throw new IllegalArgumentException(
103                     "create: handler is missing");
104         }
105         EntityManagerFactory emf = null;
106         EntityManager em = null;
107         try {
108             handler.prepare(Action.CREATE);
109             Object entity = handler.getCommonPart();
110             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entity);
111             handler.handle(Action.CREATE, wrapDoc);
112             JaxbUtils.setValue(entity, "setCreatedAtItem", Date.class, new Date());
113             emf = JpaStorageUtils.getEntityManagerFactory();
114             em = emf.createEntityManager();
115             em.getTransaction().begin();
116             em.persist(entity);
117             em.getTransaction().commit();
118             handler.complete(Action.CREATE, wrapDoc);
119             return (String) JaxbUtils.getValue(entity, "getCsid");
120         } catch (BadRequestException bre) {
121             if (em != null && em.getTransaction().isActive()) {
122                 em.getTransaction().rollback();
123             }
124             throw bre;
125         } catch (DocumentException de) {
126             if (em != null && em.getTransaction().isActive()) {
127                 em.getTransaction().rollback();
128             }
129             throw de;
130         } catch (Exception e) {
131             if (em != null && em.getTransaction().isActive()) {
132                 em.getTransaction().rollback();
133             }
134             if (logger.isDebugEnabled()) {
135                 logger.debug("Caught exception ", e);
136             }
137             throw new DocumentException(e);
138         } finally {
139             if (em != null) {
140                 JpaStorageUtils.releaseEntityManagerFactory(emf);
141             }
142         }
143
144     }
145
146     /* (non-Javadoc)
147      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
148      */
149     @Override
150     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
151             throws DocumentNotFoundException, DocumentException {
152         throw new UnsupportedOperationException();
153     }
154
155     /* (non-Javadoc)
156      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
157      */
158     @Override
159     public void get(ServiceContext ctx, String id, DocumentHandler handler)
160             throws DocumentNotFoundException, DocumentException {
161         if (ctx == null) {
162             throw new IllegalArgumentException(
163                     "get: ctx is missing");
164         }
165         if (handler == null) {
166             throw new IllegalArgumentException(
167                     "get: handler is missing");
168         }
169         EntityManagerFactory emf = null;
170         EntityManager em = null;
171         try {
172             handler.prepare(Action.GET);
173             Object o = null;
174             o = JpaStorageUtils.getEntity(getEntityName(ctx), id, 
175                     ctx.getTenantId());
176             if (null == o) {
177                 if (em != null && em.getTransaction().isActive()) {
178                     em.getTransaction().rollback();
179                 }
180                 String msg = "could not find entity with id=" + id;
181                 throw new DocumentNotFoundException(msg);
182             }
183             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(o);
184             handler.handle(Action.GET, wrapDoc);
185             handler.complete(Action.GET, wrapDoc);
186         } catch (DocumentException de) {
187             throw de;
188         } catch (Exception e) {
189             if (logger.isDebugEnabled()) {
190                 logger.debug("Caught exception ", e);
191             }
192             throw new DocumentException(e);
193         } finally {
194             if (emf != null) {
195                 JpaStorageUtils.releaseEntityManagerFactory(emf);
196             }
197         }
198     }
199
200     /* (non-Javadoc)
201      * @see org.collectionspace.services.common.storage.StorageClient#getAll(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
202      */
203     @Override
204     public void getAll(ServiceContext ctx, DocumentHandler handler)
205             throws DocumentNotFoundException, DocumentException {
206         throw new UnsupportedOperationException("use getFiltered instead");
207     }
208
209     /* (non-Javadoc)
210      * @see org.collectionspace.services.common.storage.StorageClient#getFiltered(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
211      */
212     @Override
213     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
214             throws DocumentNotFoundException, DocumentException {
215         if (ctx == null) {
216             throw new IllegalArgumentException(
217                     "getFiltered: ctx is missing");
218         }
219         if (handler == null) {
220             throw new IllegalArgumentException(
221                     "getFiltered: handler is missing");
222         }
223
224         DocumentFilter docFilter = handler.getDocumentFilter();
225         if (docFilter == null) {
226             docFilter = handler.createDocumentFilter();
227         }
228         EntityManagerFactory emf = null;
229         EntityManager em = null;
230         try {
231             handler.prepare(Action.GET_ALL);
232
233             StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
234             queryStrBldr.append(getEntityName(ctx));
235             queryStrBldr.append(" a");
236             List<DocumentFilter.ParamBinding> params = docFilter.buildWhereForSearch(queryStrBldr);
237             emf = JpaStorageUtils.getEntityManagerFactory();
238             em = emf.createEntityManager();
239             String queryStr = queryStrBldr.toString(); //for debugging
240             Query q = em.createQuery(queryStr);
241             //bind parameters
242             for (DocumentFilter.ParamBinding p : params) {
243                 q.setParameter(p.getName(), p.getValue());
244             }
245             if (docFilter.getOffset() > 0) {
246                 q.setFirstResult(docFilter.getOffset());
247             }
248             if (docFilter.getPageSize() > 0) {
249                 q.setMaxResults(docFilter.getPageSize());
250             }
251
252             //FIXME is transaction required for get?
253             em.getTransaction().begin();
254             List list = q.getResultList();
255             em.getTransaction().commit();
256             DocumentWrapper<List> wrapDoc = new DocumentWrapperImpl<List>(list);
257             handler.handle(Action.GET_ALL, wrapDoc);
258             handler.complete(Action.GET_ALL, wrapDoc);
259         } catch (DocumentException de) {
260             throw de;
261         } catch (Exception e) {
262             if (logger.isDebugEnabled()) {
263                 logger.debug("Caught exception ", e);
264             }
265             throw new DocumentException(e);
266         } finally {
267             if (emf != null) {
268                 JpaStorageUtils.releaseEntityManagerFactory(emf);
269             }
270         }
271     }
272
273     /* (non-Javadoc)
274      * @see org.collectionspace.services.common.storage.StorageClient#update(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
275      */
276     @Override
277     public void update(ServiceContext ctx, String id, DocumentHandler handler)
278             throws BadRequestException, DocumentNotFoundException,
279             DocumentException {
280         if (ctx == null) {
281             throw new IllegalArgumentException(
282                     "update: ctx is missing");
283         }
284         if (handler == null) {
285             throw new IllegalArgumentException(
286                     "update: handler is missing");
287         }
288         EntityManagerFactory emf = null;
289         EntityManager em = null;
290         try {
291             handler.prepare(Action.UPDATE);
292             Object entityReceived = handler.getCommonPart();
293             emf = JpaStorageUtils.getEntityManagerFactory();
294             em = emf.createEntityManager();
295             em.getTransaction().begin();
296             Object entityFound = getEntity(em, id, entityReceived.getClass());
297             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
298             handler.handle(Action.UPDATE, wrapDoc);
299             JaxbUtils.setValue(entityFound, "setUpdatedAtItem", Date.class, new Date());
300             em.getTransaction().commit();
301             handler.complete(Action.UPDATE, wrapDoc);
302         } catch (BadRequestException bre) {
303             if (em != null && em.getTransaction().isActive()) {
304                 em.getTransaction().rollback();
305             }
306             throw bre;
307         } catch (DocumentException de) {
308             if (em != null && em.getTransaction().isActive()) {
309                 em.getTransaction().rollback();
310             }
311             throw de;
312         } catch (Exception e) {
313             if (logger.isDebugEnabled()) {
314                 logger.debug("Caught exception ", e);
315             }
316             throw new DocumentException(e);
317         } finally {
318             if (emf != null) {
319                 JpaStorageUtils.releaseEntityManagerFactory(emf);
320             }
321         }
322     }
323
324     /* 
325      * delete removes entity and its child entities
326      * cost: a get before delete
327      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
328      */
329     @Override
330     public void delete(ServiceContext ctx, String id)
331             throws DocumentNotFoundException,
332             DocumentException {
333
334         if (logger.isDebugEnabled()) {
335             logger.debug("delete(ctx, id): deleting entity with id=" + id);
336         }
337
338         if (ctx == null) {
339             throw new IllegalArgumentException(
340                     "delete(ctx, id): ctx is missing");
341         }
342         EntityManagerFactory emf = null;
343         EntityManager em = null;
344         try {
345
346             emf = JpaStorageUtils.getEntityManagerFactory();
347             em = emf.createEntityManager();
348
349             em.getTransaction().begin();
350             Object entityFound = getEntity(ctx, em, id);
351             if (entityFound == null) {
352                 if (em != null && em.getTransaction().isActive()) {
353                     em.getTransaction().rollback();
354                 }
355                 String msg = "delete(ctx, id): could not find entity with id=" + id;
356                 logger.error(msg);
357                 throw new DocumentNotFoundException(msg);
358             }
359             em.remove(entityFound);
360             em.getTransaction().commit();
361
362         } catch (DocumentException de) {
363             if (em != null && em.getTransaction().isActive()) {
364                 em.getTransaction().rollback();
365             }
366             throw de;
367         } catch (Exception e) {
368             if (logger.isDebugEnabled()) {
369                 logger.debug("delete(ctx, id): Caught exception ", e);
370             }
371             if (em != null && em.getTransaction().isActive()) {
372                 em.getTransaction().rollback();
373             }
374             throw new DocumentException(e);
375         } finally {
376             if (emf != null) {
377                 JpaStorageUtils.releaseEntityManagerFactory(emf);
378             }
379         }
380     }
381
382     /**
383      * deleteWhere uses the where clause to delete an entityReceived represented by the csidReceived
384      * it does not delete any child entities.
385      * @param ctx
386      * @param id
387      * @throws DocumentNotFoundException
388      * @throws DocumentException
389      */
390     public void deleteWhere(ServiceContext ctx, String id)
391             throws DocumentNotFoundException,
392             DocumentException {
393
394         if (ctx == null) {
395             throw new IllegalArgumentException(
396                     "deleteWhere(ctx, id) : ctx is missing");
397         }
398
399         if (logger.isDebugEnabled()) {
400             logger.debug("deleteWhere(ctx, id): deleting entity with id=" + id);
401         }
402         EntityManagerFactory emf = null;
403         EntityManager em = null;
404         try {
405             StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
406             deleteStr.append(getEntityName(ctx));
407             deleteStr.append(" WHERE csid = :csid and tenantId = :tenantId");
408             //TODO: add tenant csidReceived
409
410             emf = JpaStorageUtils.getEntityManagerFactory();
411             em = emf.createEntityManager();
412             Query q = em.createQuery(deleteStr.toString());
413             q.setParameter("csid", id);
414             q.setParameter("tenantId", ctx.getTenantId());
415
416             int rcount = 0;
417             em.getTransaction().begin();
418             rcount = q.executeUpdate();
419             if (rcount != 1) {
420                 if (em != null && em.getTransaction().isActive()) {
421                     em.getTransaction().rollback();
422                 }
423                 String msg = "deleteWhere(ctx, id) could not find entity with id=" + id;
424                 logger.error(msg);
425                 throw new DocumentNotFoundException(msg);
426             }
427             em.getTransaction().commit();
428
429         } catch (DocumentException de) {
430             if (em != null && em.getTransaction().isActive()) {
431                 em.getTransaction().rollback();
432             }
433             throw de;
434         } catch (Exception e) {
435             if (logger.isDebugEnabled()) {
436                 logger.debug("deleteWhere(ctx, id) Caught exception ", e);
437             }
438             if (em != null && em.getTransaction().isActive()) {
439                 em.getTransaction().rollback();
440             }
441             throw new DocumentException(e);
442         } finally {
443             if (emf != null) {
444                 JpaStorageUtils.releaseEntityManagerFactory(emf);
445             }
446         }
447     }
448
449     /*
450      * delete removes entity and its child entities but calls back to given handler
451      * cost: a get before delete
452      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
453      */
454     @Override
455     public void delete(ServiceContext ctx, String id, DocumentHandler handler)
456             throws DocumentNotFoundException, DocumentException {
457         if (ctx == null) {
458             throw new IllegalArgumentException(
459                     "delete(ctx, ix, handler): ctx is missing");
460         }
461         if (handler == null) {
462             throw new IllegalArgumentException(
463                     "delete(ctx, ix, handler): handler is missing");
464         }
465         EntityManagerFactory emf = null;
466         EntityManager em = null;
467         try {
468             handler.prepare(Action.DELETE);
469
470             emf = JpaStorageUtils.getEntityManagerFactory();
471             em = emf.createEntityManager();
472
473             em.getTransaction().begin();
474             Object entityFound = getEntity(ctx, em, id);
475             if (entityFound == null) {
476                 if (em != null && em.getTransaction().isActive()) {
477                     em.getTransaction().rollback();
478                 }
479                 String msg = "delete(ctx, ix, handler) could not find entity with id=" + id;
480                 logger.error(msg);
481                 throw new DocumentNotFoundException(msg);
482             }
483             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
484             handler.handle(Action.DELETE, wrapDoc);
485             em.remove(entityFound);
486             em.getTransaction().commit();
487
488             handler.complete(Action.DELETE, wrapDoc);
489         } catch (DocumentException de) {
490             if (em != null && em.getTransaction().isActive()) {
491                 em.getTransaction().rollback();
492             }
493             throw de;
494         } catch (Exception e) {
495             if (logger.isDebugEnabled()) {
496                 logger.debug("delete(ctx, ix, handler): Caught exception ", e);
497             }
498             if (em != null && em.getTransaction().isActive()) {
499                 em.getTransaction().rollback();
500             }
501             throw new DocumentException(e);
502         } finally {
503             if (emf != null) {
504                 JpaStorageUtils.releaseEntityManagerFactory(emf);
505             }
506         }
507     }
508
509     /**
510      * Gets the entityReceived name.
511      * 
512      * @param ctx the ctx
513      * 
514      * @return the entityReceived name
515      */
516     protected String getEntityName(ServiceContext ctx) {
517         Object o = ctx.getProperty(ServiceContextProperties.ENTITY_NAME);
518         if (o == null) {
519             throw new IllegalArgumentException(ServiceContextProperties.ENTITY_NAME
520                     + "property is missing in context "
521                     + ctx.toString());
522         }
523
524         return (String) o;
525     }
526
527     /**
528      * getEntity returns persistent entity for given id. it assumes that
529      * service context has property ServiceContextProperties.ENTITY_CLASS set
530      * rolls back the transaction if not found
531      * @param ctx service context
532      * @param em entity manager
533      * @param csid received
534      * @return
535      * @throws DocumentNotFoundException and rollsback the transaction if active
536      */
537     protected Object getEntity(ServiceContext ctx, EntityManager em, String id)
538             throws DocumentNotFoundException {
539         Class entityClazz = (Class) ctx.getProperty(ServiceContextProperties.ENTITY_CLASS);
540         if (entityClazz == null) {
541             String msg = ServiceContextProperties.ENTITY_CLASS
542                     + " property is missing in the context";
543             logger.error(msg);
544             throw new IllegalArgumentException(msg);
545         }
546         return getEntity(em, id, entityClazz);
547     }
548
549     /**
550      * getEntity retrieves the persistent entity of given class for given id
551      * rolls back the transaction if not found
552      * @param em
553      * @param id entity id
554      * @param entityClazz
555      * @return
556      * @throws DocumentNotFoundException and rollsback the transaction if active
557      */
558     protected Object getEntity(EntityManager em, String id, Class entityClazz)
559             throws DocumentNotFoundException {
560         Object entityFound = JpaStorageUtils.getEntity(em, id, entityClazz);
561         if (entityFound == null) {
562             if (em != null && em.getTransaction().isActive()) {
563                 em.getTransaction().rollback();
564             }
565             String msg = "could not find entity of type=" + entityClazz.getName()
566                     + " with id=" + id;
567             logger.error(msg);
568             throw new DocumentNotFoundException(msg);
569         }
570         return entityFound;
571     }
572
573     @Override
574     public void get(ServiceContext ctx, DocumentHandler handler)
575             throws DocumentNotFoundException, DocumentException {
576         throw new UnsupportedOperationException();
577     }
578 }