]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
560c355ee4abc9f3b31789110789573317796ef2
[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 void delete(ServiceContext ctx, String id, DocumentHandler handler)
462             throws DocumentNotFoundException, DocumentException {
463         if (ctx == null) {
464             throw new IllegalArgumentException(
465                     "delete(ctx, ix, handler): ctx is missing");
466         }
467         if (handler == null) {
468             throw new IllegalArgumentException(
469                     "delete(ctx, ix, handler): handler is missing");
470         }
471         EntityManagerFactory emf = null;
472         EntityManager em = null;
473         try {
474             handler.prepare(Action.DELETE);
475
476             emf = JpaStorageUtils.getEntityManagerFactory();
477             em = emf.createEntityManager();
478
479             em.getTransaction().begin();
480             Object entityFound = getEntity(ctx, em, id);
481             if (entityFound == null) {
482                 if (em != null && em.getTransaction().isActive()) {
483                     em.getTransaction().rollback();
484                 }
485                 String msg = "delete(ctx, ix, handler) could not find entity with id=" + id;
486                 logger.error(msg);
487                 throw new DocumentNotFoundException(msg);
488             }
489             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
490             handler.handle(Action.DELETE, wrapDoc);
491             em.remove(entityFound);
492             em.getTransaction().commit();
493
494             handler.complete(Action.DELETE, wrapDoc);
495         } catch (DocumentException de) {
496             if (em != null && em.getTransaction().isActive()) {
497                 em.getTransaction().rollback();
498             }
499             throw de;
500         } catch (Exception e) {
501             if (logger.isDebugEnabled()) {
502                 logger.debug("delete(ctx, ix, handler): Caught exception ", e);
503             }
504             if (em != null && em.getTransaction().isActive()) {
505                 em.getTransaction().rollback();
506             }
507             throw new DocumentException(e);
508         } finally {
509             if (emf != null) {
510                 JpaStorageUtils.releaseEntityManagerFactory(emf);
511             }
512         }
513     }
514
515     /**
516      * Gets the entityReceived name.
517      * 
518      * @param ctx the ctx
519      * 
520      * @return the entityReceived name
521      */
522     protected String getEntityName(ServiceContext ctx) {
523         Object o = ctx.getProperty(ServiceContextProperties.ENTITY_NAME);
524         if (o == null) {
525             throw new IllegalArgumentException(ServiceContextProperties.ENTITY_NAME
526                     + "property is missing in context "
527                     + ctx.toString());
528         }
529
530         return (String) o;
531     }
532
533     /**
534      * getEntity returns persistent entity for given id. it assumes that
535      * service context has property ServiceContextProperties.ENTITY_CLASS set
536      * rolls back the transaction if not found
537      * @param ctx service context
538      * @param em entity manager
539      * @param csid received
540      * @return
541      * @throws DocumentNotFoundException and rollsback the transaction if active
542      */
543     protected Object getEntity(ServiceContext ctx, EntityManager em, String id)
544             throws DocumentNotFoundException {
545         Class entityClazz = (Class) ctx.getProperty(ServiceContextProperties.ENTITY_CLASS);
546         if (entityClazz == null) {
547             String msg = ServiceContextProperties.ENTITY_CLASS
548                     + " property is missing in the context";
549             logger.error(msg);
550             throw new IllegalArgumentException(msg);
551         }
552         return getEntity(em, id, entityClazz);
553     }
554
555     /**
556      * getEntity retrieves the persistent entity of given class for given id
557      * rolls back the transaction if not found
558      * @param em
559      * @param id entity id
560      * @param entityClazz
561      * @return
562      * @throws DocumentNotFoundException and rollsback the transaction if active
563      */
564     protected Object getEntity(EntityManager em, String id, Class entityClazz)
565             throws DocumentNotFoundException {
566         Object entityFound = JpaStorageUtils.getEntity(em, id, entityClazz);
567         if (entityFound == null) {
568             if (em != null && em.getTransaction().isActive()) {
569                 em.getTransaction().rollback();
570             }
571             String msg = "could not find entity of type=" + entityClazz.getName()
572                     + " with id=" + id;
573             logger.error(msg);
574             throw new DocumentNotFoundException(msg);
575         }
576         return entityFound;
577     }
578
579     @Override
580     public void get(ServiceContext ctx, DocumentHandler handler)
581             throws DocumentNotFoundException, DocumentException {
582         throw new UnsupportedOperationException();
583     }
584
585         @Override
586         public void doWorkflowTransition(ServiceContext ctx, String id,
587                         DocumentHandler handler, TransitionDef transitionDef)
588                         throws BadRequestException, DocumentNotFoundException,
589                         DocumentException {
590                 // Do nothing.  JPA services do not support workflow.
591         }
592
593         @Override
594         public void deleteWithWhereClause(ServiceContext ctx, String whereClause,
595                         DocumentHandler handler) throws DocumentNotFoundException,
596                         DocumentException {
597         throw new UnsupportedOperationException();
598         }
599
600         @Override
601         public boolean synchronize(ServiceContext ctx, Object specifier,
602                         DocumentHandler handler) throws DocumentNotFoundException,
603                         TransactionException, DocumentException {
604                 // TODO Auto-generated method stub
605                 // Do nothing. Subclasses can override if they want/need to.
606                 return true;
607         }
608         
609         @Override
610         public boolean synchronizeItem(ServiceContext ctx, AuthorityItemSpecifier itemSpecifier,
611                         DocumentHandler handler) throws DocumentNotFoundException,
612                         TransactionException, DocumentException {
613                 // TODO Auto-generated method stub
614                 // Do nothing. Subclasses can override if they want/need to.
615                 return true;
616         }
617
618 }