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