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