]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
6fcb2b57719d5e72d2b4a8f22da212b401c8839c
[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
87     /**
88      * Instantiates a new jpa storage client.
89      */
90     public JpaStorageClientImpl() {
91     }
92
93     /* (non-Javadoc)
94      * @see org.collectionspace.services.common.storage.StorageClient#create(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
95      */
96     @Override
97     public String create(ServiceContext ctx,
98             DocumentHandler handler) throws BadRequestException,
99             DocumentException {
100
101         String docType = ctx.getDocumentType();
102         if (docType == null) {
103             throw new DocumentNotFoundException(
104                     "Unable to find DocumentType for service " + ctx.getServiceName());
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         String docType = ctx.getDocumentType();
171         if (docType == null) {
172             throw new DocumentNotFoundException(
173                     "Unable to find DocumentType for service " + ctx.getServiceName());
174         }
175         EntityManagerFactory emf = null;
176         EntityManager em = null;
177         try {
178             handler.prepare(Action.GET);
179             StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
180             queryStrBldr.append(getEntityName(ctx));
181             queryStrBldr.append(" a");
182             queryStrBldr.append(" WHERE csid = :csid");
183             //TODO: add tenant id
184             String where = docFilter.getWhereClause();
185             if ((null != where) && (where.length() > 0)) {
186                 queryStrBldr.append(" AND " + where);
187             }
188             emf = getEntityManagerFactory();
189             em = emf.createEntityManager();
190             String queryStr = queryStrBldr.toString(); //for debugging
191             Query q = em.createQuery(queryStr);
192             q.setParameter("csid", id);
193             //TODO: add tenant id
194
195             //TODO: get page
196             if ((docFilter.getOffset() > 0) || (docFilter.getPageSize() > 0)) {
197             } else {
198             }
199             Object o = null;
200
201             try {
202                 //require transaction for get?
203                 em.getTransaction().begin();
204                 o = q.getSingleResult();
205                 em.getTransaction().commit();
206             } catch (NoResultException nre) {
207                 if (em != null && em.getTransaction().isActive()) {
208                     em.getTransaction().rollback();
209                 }
210                 String msg = "could not find entity with id=" + id;
211                 logger.error(msg, nre);
212                 throw new DocumentNotFoundException(msg, nre);
213             }
214             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(o);
215             handler.handle(Action.GET, wrapDoc);
216             handler.complete(Action.GET, wrapDoc);
217         } catch (DocumentException de) {
218             throw de;
219         } catch (Exception e) {
220             if (logger.isDebugEnabled()) {
221                 logger.debug("Caught exception ", e);
222             }
223             throw new DocumentException(e);
224         } finally {
225             if (emf != null) {
226                 releaseEntityManagerFactory(emf);
227             }
228         }
229     }
230
231     /* (non-Javadoc)
232      * @see org.collectionspace.services.common.storage.StorageClient#getAll(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
233      */
234     @Override
235     public void getAll(ServiceContext ctx, DocumentHandler handler)
236             throws DocumentNotFoundException, DocumentException {
237         throw new UnsupportedOperationException("use getFiltered instead");
238     }
239
240     /* (non-Javadoc)
241      * @see org.collectionspace.services.common.storage.StorageClient#getFiltered(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
242      */
243     @Override
244     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
245             throws DocumentNotFoundException, DocumentException {
246         if (handler == null) {
247             throw new IllegalArgumentException(
248                     "JpaStorageClient.getFiltered: handler is missing");
249         }
250         DocumentFilter docFilter = handler.getDocumentFilter();
251         if (docFilter == null) {
252             docFilter = handler.createDocumentFilter(ctx);
253         }
254         String docType = ctx.getDocumentType();
255         if (docType == null) {
256             throw new DocumentNotFoundException(
257                     "Unable to find DocumentType for service " + ctx.getServiceName());
258         }
259
260         EntityManagerFactory emf = null;
261         EntityManager em = null;
262         try {
263             handler.prepare(Action.GET_ALL);
264
265             StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
266             queryStrBldr.append(getEntityName(ctx));
267             queryStrBldr.append(" a");
268             List<DocumentFilter.ParamBinding> params = docFilter.buildWhereForSearch(queryStrBldr);
269             //TODO: add tenant id
270             emf = getEntityManagerFactory();
271             em = emf.createEntityManager();
272             String queryStr = queryStrBldr.toString(); //for debugging
273             Query q = em.createQuery(queryStr);
274             //bind parameters
275             for (DocumentFilter.ParamBinding p : params) {
276                 q.setParameter(p.getName(), p.getValue());
277             }
278             if (docFilter.getOffset() > 0) {
279                 q.setFirstResult(docFilter.getOffset());
280             }
281             if (docFilter.getPageSize() > 0) {
282                 q.setMaxResults(docFilter.getPageSize());
283             }
284
285             //FIXME is transaction required for get?
286             em.getTransaction().begin();
287             List list = q.getResultList();
288             em.getTransaction().commit();
289             DocumentWrapper<List> wrapDoc = new DocumentWrapperImpl<List>(list);
290             handler.handle(Action.GET_ALL, wrapDoc);
291             handler.complete(Action.GET_ALL, wrapDoc);
292         } catch (DocumentException de) {
293             throw de;
294         } catch (Exception e) {
295             if (logger.isDebugEnabled()) {
296                 logger.debug("Caught exception ", e);
297             }
298             throw new DocumentException(e);
299         } finally {
300             if (emf != null) {
301                 releaseEntityManagerFactory(emf);
302             }
303         }
304     }
305
306     /* (non-Javadoc)
307      * @see org.collectionspace.services.common.storage.StorageClient#update(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
308      */
309     @Override
310     public void update(ServiceContext ctx, String id, DocumentHandler handler)
311             throws BadRequestException, DocumentNotFoundException,
312             DocumentException {
313         String docType = ctx.getDocumentType();
314         if (docType == null) {
315             throw new DocumentNotFoundException(
316                     "Unable to find DocumentType for service " + ctx.getServiceName());
317         }
318         if (handler == null) {
319             throw new IllegalArgumentException(
320                     "JpaStorageClient.update: handler is missing");
321         }
322         EntityManagerFactory emf = null;
323         EntityManager em = null;
324         try {
325             handler.prepare(Action.UPDATE);
326             Object entity = handler.getCommonPart();
327             setCsid(entity, id);
328             setValue(entity, "setUpdatedAtItem", Date.class, new Date());
329             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entity);
330             handler.handle(Action.UPDATE, wrapDoc);
331             emf = getEntityManagerFactory();
332             em = emf.createEntityManager();
333             em.getTransaction().begin();
334             Object entityFound = em.find(entity.getClass(), id);
335             if (entityFound == null) {
336                 if (em != null && em.getTransaction().isActive()) {
337                     em.getTransaction().rollback();
338                 }
339                 String msg = "could not find entity with id=" + id;
340                 logger.error(msg);
341                 throw new DocumentNotFoundException(msg);
342             }
343             em.merge(entity);
344             em.getTransaction().commit();
345             handler.complete(Action.UPDATE, wrapDoc);
346         } catch (BadRequestException bre) {
347             if (em != null && em.getTransaction().isActive()) {
348                 em.getTransaction().rollback();
349             }
350             throw bre;
351         } catch (DocumentException de) {
352             throw de;
353         } catch (Exception e) {
354             if (logger.isDebugEnabled()) {
355                 logger.debug("Caught exception ", e);
356             }
357             throw new DocumentException(e);
358         } finally {
359             if (emf != null) {
360                 releaseEntityManagerFactory(emf);
361             }
362         }
363     }
364
365     /* (non-Javadoc)
366      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
367      */
368     @Override
369     public void delete(ServiceContext ctx, String id)
370             throws DocumentNotFoundException,
371             DocumentException {
372
373         if (logger.isDebugEnabled()) {
374             logger.debug("deleting entity with id=" + id);
375         }
376         String docType = ctx.getDocumentType();
377         if (docType == null) {
378             throw new DocumentNotFoundException(
379                     "Unable to find DocumentType for service " + ctx.getServiceName());
380         }
381         EntityManagerFactory emf = null;
382         EntityManager em = null;
383         try {
384             StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
385             deleteStr.append(getEntityName(ctx));
386             deleteStr.append(" WHERE csid = :csid");
387             //TODO: add tenant id
388
389             emf = getEntityManagerFactory();
390             em = emf.createEntityManager();
391             Query q = em.createQuery(deleteStr.toString());
392             q.setParameter("csid", id);
393             //TODO: add tenant id
394             int rcount = 0;
395             em.getTransaction().begin();
396             rcount = q.executeUpdate();
397             if (rcount != 1) {
398                 if (em != null && em.getTransaction().isActive()) {
399                     em.getTransaction().rollback();
400                 }
401                 String msg = "could not find entity with id=" + id;
402                 logger.error(msg);
403                 throw new DocumentNotFoundException(msg);
404             }
405             em.getTransaction().commit();
406
407         } catch (DocumentException de) {
408             throw de;
409         } catch (Exception e) {
410             if (logger.isDebugEnabled()) {
411                 logger.debug("Caught exception ", e);
412             }
413             if (em != null && em.getTransaction().isActive()) {
414                 em.getTransaction().rollback();
415             }
416             throw new DocumentException(e);
417         } finally {
418             if (emf != null) {
419                 releaseEntityManagerFactory(emf);
420             }
421         }
422     }
423
424     /**
425      * Gets the entity manager factory.
426      * 
427      * @return the entity manager factory
428      */
429     public EntityManagerFactory getEntityManagerFactory() {
430         return getEntityManagerFactory(CS_PERSISTENCE_UNIT);
431     }
432
433     /**
434      * Gets the entity manager factory.
435      * 
436      * @param persistenceUnit the persistence unit
437      * 
438      * @return the entity manager factory
439      */
440     public EntityManagerFactory getEntityManagerFactory(
441             String persistenceUnit) {
442         return Persistence.createEntityManagerFactory(persistenceUnit);
443
444     }
445
446     /**
447      * Release entity manager factory.
448      * 
449      * @param emf the emf
450      */
451     public void releaseEntityManagerFactory(EntityManagerFactory emf) {
452         if (emf != null) {
453             emf.close();
454         }
455
456     }
457
458     /**
459      * getValue gets invokes specified accessor method on given object. Assumption
460      * is that this is used for JavaBean pattern getXXX methods only.
461      * @param o object to return value from
462      * @param methodName of method to invoke
463      * @return value returned of invocation
464      * @throws NoSuchMethodException
465      * @throws IllegalAccessException
466      * @throws InvocationTargetException
467      */
468     protected Object getValue(Object o, String methodName)
469             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
470         if (methodName == null) {
471             String msg = methodName + " cannot be null";
472             logger.error(msg);
473             throw new IllegalArgumentException(msg);
474         }
475         Class c = o.getClass();
476         Method m = c.getMethod(methodName);
477
478         Object r = m.invoke(o);
479         if (logger.isDebugEnabled()) {
480             logger.debug("getValue returned value=" + r
481                     + " for " + c.getName());
482         }
483         return r;
484     }
485
486     /**
487      * setValue mutates the given object by invoking specified method. Assumption
488      * is that this is used for JavaBean pattern setXXX methods only.
489      * @param o object to mutate
490      * @param methodName indicates method to invoke
491      * @param argType type of the only argument (assumed) to method
492      * @param argValue value of the only argument (assumed) to method
493      * @return
494      * @throws NoSuchMethodException
495      * @throws IllegalAccessException
496      * @throws InvocationTargetException
497      */
498     protected Object setValue(Object o, String methodName, Class argType, Object argValue)
499             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
500         if (methodName == null) {
501             String msg = methodName + " cannot be null";
502             logger.error(msg);
503             throw new IllegalArgumentException(msg);
504         }
505         if (argType == null) {
506             String msg = "argType cannot be null";
507             logger.error(msg);
508             throw new IllegalArgumentException(msg);
509         }
510         Class c = o.getClass();
511         Method m = c.getMethod(methodName, argType);
512         Object r = m.invoke(o, argValue);
513         if (logger.isDebugEnabled()) {
514             logger.debug("completed invocation of " + methodName
515                     + " for " + c.getName());
516         }
517         return r;
518     }
519
520     /**
521      * Sets the csid.
522      * 
523      * @param o the o
524      * @param csid the csid
525      * 
526      * @throws Exception the exception
527      */
528     protected void setCsid(Object o, String csid) throws Exception {
529         //verify csid
530         String id = (String) getValue(o, "getCsid");
531         if (id != null) {
532             if (!id.equals(csid)) {
533                 String msg = "Csids do not match!";
534                 logger.error(msg);
535                 throw new BadRequestException(msg);
536             } else {
537                 //no need to set
538                 return;
539             }
540
541         }
542         //set csid
543         setValue(o, "setCsid", java.lang.String.class, csid);
544     }
545
546     /**
547      * Gets the entity name.
548      * 
549      * @param ctx the ctx
550      * 
551      * @return the entity name
552      */
553     protected String getEntityName(ServiceContext ctx) {
554         Object o = ctx.getProperty("entity-name");
555         if (o == null) {
556             throw new IllegalArgumentException("property entity-name missing in context "
557                     + ctx.toString());
558         }
559
560         return (String) o;
561     }
562
563     @Override
564     public void get(ServiceContext ctx, DocumentHandler handler)
565             throws DocumentNotFoundException, DocumentException {
566         throw new UnsupportedOperationException();
567     }
568 }