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