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