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