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