]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
e4adc202a944397fe5aee68d08272c8f8874b9a0
[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="csidFound" type="xs:string">
67 <xs:annotation>
68 <xs:appinfo>
69 <hj:csidReceived>
70 <orm:column name="csidFound" 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             throw de;
127         } catch (Exception e) {
128             if (em != null && em.getTransaction().isActive()) {
129                 em.getTransaction().rollback();
130             }
131             if (logger.isDebugEnabled()) {
132                 logger.debug("Caught exception ", e);
133             }
134             throw new DocumentException(e);
135         } finally {
136             if (em != null) {
137                 JpaStorageUtils.releaseEntityManagerFactory(emf);
138             }
139         }
140
141     }
142
143     /* (non-Javadoc)
144      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
145      */
146     @Override
147     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
148             throws DocumentNotFoundException, DocumentException {
149         throw new UnsupportedOperationException();
150     }
151
152     /* (non-Javadoc)
153      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
154      */
155     @Override
156     public void get(ServiceContext ctx, String id, DocumentHandler handler)
157             throws DocumentNotFoundException, DocumentException {
158         if (ctx == null) {
159             throw new IllegalArgumentException(
160                     "JpaStorageClient.get: ctx is missing");
161         }
162         if (handler == null) {
163             throw new IllegalArgumentException(
164                     "JpaStorageClient.get: handler is missing");
165         }
166         DocumentFilter docFilter = handler.getDocumentFilter();
167         if (docFilter == null) {
168             docFilter = handler.createDocumentFilter();
169         }
170         EntityManagerFactory emf = null;
171         EntityManager em = null;
172         try {
173             handler.prepare(Action.GET);
174             Object o = null;
175             o = JpaStorageUtils.getEntity(getEntityName(ctx), id, docFilter);
176             if (null == o) {
177                 if (em != null && em.getTransaction().isActive()) {
178                     em.getTransaction().rollback();
179                 }
180                 String msg = "could not find entity with id=" + id;
181                 throw new DocumentNotFoundException(msg);
182             }
183             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(o);
184             handler.handle(Action.GET, wrapDoc);
185             handler.complete(Action.GET, wrapDoc);
186         } catch (DocumentException de) {
187             throw de;
188         } catch (Exception e) {
189             if (logger.isDebugEnabled()) {
190                 logger.debug("Caught exception ", e);
191             }
192             throw new DocumentException(e);
193         } finally {
194             if (emf != null) {
195                 JpaStorageUtils.releaseEntityManagerFactory(emf);
196             }
197         }
198     }
199
200     /* (non-Javadoc)
201      * @see org.collectionspace.services.common.storage.StorageClient#getAll(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
202      */
203     @Override
204     public void getAll(ServiceContext ctx, DocumentHandler handler)
205             throws DocumentNotFoundException, DocumentException {
206         throw new UnsupportedOperationException("use getFiltered instead");
207     }
208
209     /* (non-Javadoc)
210      * @see org.collectionspace.services.common.storage.StorageClient#getFiltered(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
211      */
212     @Override
213     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
214             throws DocumentNotFoundException, DocumentException {
215         if (ctx == null) {
216             throw new IllegalArgumentException(
217                     "JpaStorageClient.getFiltered: ctx is missing");
218         }
219         if (handler == null) {
220             throw new IllegalArgumentException(
221                     "JpaStorageClient.getFiltered: handler is missing");
222         }
223
224         DocumentFilter docFilter = handler.getDocumentFilter();
225         if (docFilter == null) {
226             docFilter = handler.createDocumentFilter();
227         }
228         EntityManagerFactory emf = null;
229         EntityManager em = null;
230         try {
231             handler.prepare(Action.GET_ALL);
232
233             StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
234             queryStrBldr.append(getEntityName(ctx));
235             queryStrBldr.append(" a");
236             List<DocumentFilter.ParamBinding> params = docFilter.buildWhereForSearch(queryStrBldr);
237             //TODO: add tenant csidReceived
238             emf = JpaStorageUtils.getEntityManagerFactory();
239             em = emf.createEntityManager();
240             String queryStr = queryStrBldr.toString(); //for debugging
241             Query q = em.createQuery(queryStr);
242             //bind parameters
243             for (DocumentFilter.ParamBinding p : params) {
244                 q.setParameter(p.getName(), p.getValue());
245             }
246             if (docFilter.getOffset() > 0) {
247                 q.setFirstResult(docFilter.getOffset());
248             }
249             if (docFilter.getPageSize() > 0) {
250                 q.setMaxResults(docFilter.getPageSize());
251             }
252
253             //FIXME is transaction required for get?
254             em.getTransaction().begin();
255             List list = q.getResultList();
256             em.getTransaction().commit();
257             DocumentWrapper<List> wrapDoc = new DocumentWrapperImpl<List>(list);
258             handler.handle(Action.GET_ALL, wrapDoc);
259             handler.complete(Action.GET_ALL, wrapDoc);
260         } catch (DocumentException de) {
261             throw de;
262         } catch (Exception e) {
263             if (logger.isDebugEnabled()) {
264                 logger.debug("Caught exception ", e);
265             }
266             throw new DocumentException(e);
267         } finally {
268             if (emf != null) {
269                 JpaStorageUtils.releaseEntityManagerFactory(emf);
270             }
271         }
272     }
273
274     /* (non-Javadoc)
275      * @see org.collectionspace.services.common.storage.StorageClient#update(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
276      */
277     @Override
278     public void update(ServiceContext ctx, String id, DocumentHandler handler)
279             throws BadRequestException, DocumentNotFoundException,
280             DocumentException {
281         if (ctx == null) {
282             throw new IllegalArgumentException(
283                     "JpaStorageClient.update: ctx is missing");
284         }
285         if (handler == null) {
286             throw new IllegalArgumentException(
287                     "JpaStorageClient.update: handler is missing");
288         }
289         EntityManagerFactory emf = null;
290         EntityManager em = null;
291         try {
292             handler.prepare(Action.UPDATE);
293             Object entityReceived = handler.getCommonPart();
294             emf = JpaStorageUtils.getEntityManagerFactory();
295             em = emf.createEntityManager();
296             em.getTransaction().begin();
297             Object entityFound = getEntity(em, id, entityReceived.getClass());
298             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
299             handler.handle(Action.UPDATE, wrapDoc);
300             em.getTransaction().commit();
301             handler.complete(Action.UPDATE, wrapDoc);
302         } catch (BadRequestException bre) {
303             if (em != null && em.getTransaction().isActive()) {
304                 em.getTransaction().rollback();
305             }
306             throw bre;
307         } catch (DocumentException de) {
308             throw de;
309         } catch (Exception e) {
310             if (logger.isDebugEnabled()) {
311                 logger.debug("Caught exception ", e);
312             }
313             throw new DocumentException(e);
314         } finally {
315             if (emf != null) {
316                 JpaStorageUtils.releaseEntityManagerFactory(emf);
317             }
318         }
319     }
320
321     /* 
322      * delete removes entity and its child entities
323      * cost: a get before delete
324      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
325      */
326     @Override
327     public void delete(ServiceContext ctx, String id)
328             throws DocumentNotFoundException,
329             DocumentException {
330
331         if (logger.isDebugEnabled()) {
332             logger.debug("deleting entity with id=" + id);
333         }
334
335         if (ctx == null) {
336             throw new IllegalArgumentException(
337                     "JpaStorageClient.delete: ctx is missing");
338         }
339         EntityManagerFactory emf = null;
340         EntityManager em = null;
341         try {
342
343             emf = JpaStorageUtils.getEntityManagerFactory();
344             em = emf.createEntityManager();
345
346             em.getTransaction().begin();
347             Object entityFound = getEntity(ctx, em, id);
348             if (entityFound == null) {
349                 if (em != null && em.getTransaction().isActive()) {
350                     em.getTransaction().rollback();
351                 }
352                 String msg = "could not find entity with id=" + id;
353                 logger.error(msg);
354                 throw new DocumentNotFoundException(msg);
355             }
356             em.remove(entityFound);
357             em.getTransaction().commit();
358
359         } catch (DocumentException de) {
360             throw de;
361         } catch (Exception e) {
362             if (logger.isDebugEnabled()) {
363                 logger.debug("Caught exception ", e);
364             }
365             if (em != null && em.getTransaction().isActive()) {
366                 em.getTransaction().rollback();
367             }
368             throw new DocumentException(e);
369         } finally {
370             if (emf != null) {
371                 JpaStorageUtils.releaseEntityManagerFactory(emf);
372             }
373         }
374     }
375
376     /**
377      * deleteWhere uses the where clause to delete an entityReceived represented by the csidReceived
378      * it does not delete any child entities.
379      * @param ctx
380      * @param id
381      * @throws DocumentNotFoundException
382      * @throws DocumentException
383      */
384     public void deleteWhere(ServiceContext ctx, String id)
385             throws DocumentNotFoundException,
386             DocumentException {
387
388         if (ctx == null) {
389             throw new IllegalArgumentException(
390                     "JpaStorageClient.deleteWhere: ctx is missing");
391         }
392
393         if (logger.isDebugEnabled()) {
394             logger.debug("deleting entity with id=" + id);
395         }
396         EntityManagerFactory emf = null;
397         EntityManager em = null;
398         try {
399             StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
400             deleteStr.append(getEntityName(ctx));
401             deleteStr.append(" WHERE csid = :csid");
402             //TODO: add tenant csidReceived
403
404             emf = JpaStorageUtils.getEntityManagerFactory();
405             em = emf.createEntityManager();
406             Query q = em.createQuery(deleteStr.toString());
407             q.setParameter("csid", id);
408
409             int rcount = 0;
410             em.getTransaction().begin();
411             rcount = q.executeUpdate();
412             if (rcount != 1) {
413                 if (em != null && em.getTransaction().isActive()) {
414                     em.getTransaction().rollback();
415                 }
416                 String msg = "could not find entity with id=" + id;
417                 logger.error(msg);
418                 throw new DocumentNotFoundException(msg);
419             }
420             em.getTransaction().commit();
421
422         } catch (DocumentException de) {
423             throw de;
424         } catch (Exception e) {
425             if (logger.isDebugEnabled()) {
426                 logger.debug("Caught exception ", e);
427             }
428             if (em != null && em.getTransaction().isActive()) {
429                 em.getTransaction().rollback();
430             }
431             throw new DocumentException(e);
432         } finally {
433             if (emf != null) {
434                 JpaStorageUtils.releaseEntityManagerFactory(emf);
435             }
436         }
437     }
438
439     @Override
440     public void delete(ServiceContext ctx, String id, DocumentHandler handler)
441             throws DocumentNotFoundException, DocumentException {
442         if (ctx == null) {
443             throw new IllegalArgumentException(
444                     "JpaStorageClient.delete: ctx is missing");
445         }
446         if (handler == null) {
447             throw new IllegalArgumentException(
448                     "JpaStorageClient.delete: handler is missing");
449         }
450         EntityManagerFactory emf = null;
451         EntityManager em = null;
452         try {
453             handler.prepare(Action.DELETE);
454             Object entity = handler.getCommonPart();
455             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entity);
456
457             handler.handle(Action.DELETE, wrapDoc);
458             handler.complete(Action.DELETE, wrapDoc);
459         } catch (DocumentException de) {
460             throw de;
461         } catch (Exception e) {
462             if (logger.isDebugEnabled()) {
463                 logger.debug("Caught exception ", e);
464             }
465             if (em != null && em.getTransaction().isActive()) {
466                 em.getTransaction().rollback();
467             }
468             throw new DocumentException(e);
469         } finally {
470             if (emf != null) {
471                 JpaStorageUtils.releaseEntityManagerFactory(emf);
472             }
473         }
474     }
475
476     /**
477      * Gets the entityReceived name.
478      * 
479      * @param ctx the ctx
480      * 
481      * @return the entityReceived name
482      */
483     protected String getEntityName(ServiceContext ctx) {
484         Object o = ctx.getProperty(ServiceContextProperties.ENTITY_NAME);
485         if (o == null) {
486             throw new IllegalArgumentException(ServiceContextProperties.ENTITY_NAME
487                     + "property is missing in context "
488                     + ctx.toString());
489         }
490
491         return (String) o;
492     }
493
494     /**
495      * getEntity returns persistent entity for given id. it assumes that
496      * service context has property ServiceContextProperties.ENTITY_CLASS set
497      * @param ctx service context
498      * @param em entity manager
499      * @param csid received
500      * @return
501      * @throws DocumentNotFoundException and rollsback the transaction if active
502      */
503     protected Object getEntity(ServiceContext ctx, EntityManager em, String id)
504             throws DocumentNotFoundException {
505         Class entityClazz = (Class) ctx.getProperty(ServiceContextProperties.ENTITY_CLASS);
506         if (entityClazz == null) {
507             String msg = ServiceContextProperties.ENTITY_CLASS
508                     + " property is missing in the context";
509             logger.error(msg);
510             throw new IllegalArgumentException(msg);
511         }
512         return getEntity(em, id, entityClazz);
513     }
514
515     /**
516      * getEntity retrieves the persistent entity of given class for given id
517      * @param em
518      * @param id entity id
519      * @param entityClazz
520      * @return
521      * @throws DocumentNotFoundException and rollsback the transaction if active
522      */
523     protected Object getEntity(EntityManager em, String id, Class entityClazz)
524             throws DocumentNotFoundException {
525         Object entityFound = JpaStorageUtils.getEntity(em, id, entityClazz);
526         if (entityFound == null) {
527             if (em != null && em.getTransaction().isActive()) {
528                 em.getTransaction().rollback();
529             }
530             String msg = "could not find entity of type=" + entityClazz.getName()
531                     + " with id=" + id;
532             logger.error(msg);
533             throw new DocumentNotFoundException(msg);
534         }
535         return entityFound;
536     }
537
538     @Override
539     public void get(ServiceContext ctx, DocumentHandler handler)
540             throws DocumentNotFoundException, DocumentException {
541         throw new UnsupportedOperationException();
542     }
543 }