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