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