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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 package org.collectionspace.services.common.storage.jpa;
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;
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.
47 * $LastChangedRevision: $ $LastChangedDate: $
49 public class JpaStorageClientImpl implements StorageClient {
52 private final Logger logger = LoggerFactory.getLogger(JpaStorageClientImpl.class);
54 /** The Constant CS_PERSISTENCE_UNIT. */
55 public final static String CS_PERSISTENCE_UNIT = "org.collectionspace.services";
58 * Instantiates a new jpa storage client.
60 public JpaStorageClientImpl() {
64 * @see org.collectionspace.services.common.storage.StorageClient#create(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
67 public String create(ServiceContext ctx,
68 DocumentHandler handler) throws BadRequestException,
71 String docType = ctx.getDocumentType();
72 if (docType == null) {
73 throw new DocumentNotFoundException(
74 "Unable to find DocumentType for service " + ctx.getServiceName());
76 if (handler == null) {
77 throw new IllegalArgumentException(
78 "JpaStorageClient.create: handler is missing");
80 EntityManagerFactory emf = null;
81 EntityManager em = null;
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();
91 em.getTransaction().commit();
92 handler.complete(Action.CREATE, wrapDoc);
93 return (String) getValue(entity, "getCsid");
94 } catch (DocumentException de) {
96 } catch (Exception e) {
97 if (em != null && em.getTransaction().isActive()) {
98 em.getTransaction().rollback();
100 if (logger.isDebugEnabled()) {
101 logger.debug("Caught exception ", e);
103 throw new DocumentException(e);
106 releaseEntityManagerFactory(emf);
113 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
115 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
116 throws DocumentNotFoundException, DocumentException {
117 throw new UnsupportedOperationException();
121 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
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");
130 DocumentFilter docFilter = handler.createDocumentFilter(ctx);
131 if (docFilter == null) {
132 throw new IllegalArgumentException(
133 "JpaStorageClient.get: handler has no Filter specified");
135 String docType = ctx.getDocumentType();
136 if (docType == null) {
137 throw new DocumentNotFoundException(
138 "Unable to find DocumentType for service " + ctx.getServiceName());
140 EntityManagerFactory emf = null;
141 EntityManager em = null;
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);
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
161 if ((docFilter.getOffset() > 0) || (docFilter.getPageSize() > 0)) {
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();
175 String msg = "could not find entity with id=" + id;
176 logger.error(msg, nre);
177 throw new DocumentNotFoundException(msg, nre);
179 DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(o);
180 handler.handle(Action.GET, wrapDoc);
181 handler.complete(Action.GET, wrapDoc);
182 } catch (DocumentException de) {
184 } catch (Exception e) {
185 if (logger.isDebugEnabled()) {
186 logger.debug("Caught exception ", e);
188 throw new DocumentException(e);
191 releaseEntityManagerFactory(emf);
197 * @see org.collectionspace.services.common.storage.StorageClient#getAll(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
200 public void getAll(ServiceContext ctx, DocumentHandler handler)
201 throws DocumentNotFoundException, DocumentException {
202 throw new UnsupportedOperationException("use getFiltered instead");
206 * @see org.collectionspace.services.common.storage.StorageClient#getFiltered(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
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");
215 DocumentFilter docFilter = handler.createDocumentFilter(ctx);
216 if (docFilter == null) {
217 throw new IllegalArgumentException(
218 "JpaStorageClient.getFiltered: handler has no Filter specified");
220 String docType = ctx.getDocumentType();
221 if (docType == null) {
222 throw new DocumentNotFoundException(
223 "Unable to find DocumentType for service " + ctx.getServiceName());
226 EntityManagerFactory emf = null;
227 EntityManager em = null;
229 handler.prepare(Action.GET_ALL);
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);
241 for(DocumentFilter.ParamBinding p : params) {
242 q.setParameter(p.getName(), p.getValue());
244 if (docFilter.getOffset() > 0) {
245 q.setFirstResult(docFilter.getOffset());
247 if (docFilter.getPageSize() > 0) {
248 q.setMaxResults(docFilter.getPageSize());
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) {
260 } catch (Exception e) {
261 if (logger.isDebugEnabled()) {
262 logger.debug("Caught exception ", e);
264 throw new DocumentException(e);
267 releaseEntityManagerFactory(emf);
273 * @see org.collectionspace.services.common.storage.StorageClient#update(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
276 public void update(ServiceContext ctx, String id, DocumentHandler handler)
277 throws BadRequestException, DocumentNotFoundException,
279 String docType = ctx.getDocumentType();
280 if (docType == null) {
281 throw new DocumentNotFoundException(
282 "Unable to find DocumentType for service " + ctx.getServiceName());
284 if (handler == null) {
285 throw new IllegalArgumentException(
286 "JpaStorageClient.update: handler is missing");
288 EntityManagerFactory emf = null;
289 EntityManager em = null;
291 handler.prepare(Action.UPDATE);
292 Object entity = handler.getCommonPart();
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();
304 String msg = "could not find entity with id=" + id;
306 throw new DocumentNotFoundException(msg);
309 em.getTransaction().commit();
310 handler.complete(Action.UPDATE, wrapDoc);
311 } catch (DocumentException de) {
313 } catch (Exception e) {
314 if (logger.isDebugEnabled()) {
315 logger.debug("Caught exception ", e);
317 throw new DocumentException(e);
320 releaseEntityManagerFactory(emf);
326 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
329 public void delete(ServiceContext ctx, String id)
330 throws DocumentNotFoundException,
333 if (logger.isDebugEnabled()) {
334 logger.debug("deleting entity with id=" + id);
336 String docType = ctx.getDocumentType();
337 if (docType == null) {
338 throw new DocumentNotFoundException(
339 "Unable to find DocumentType for service " + ctx.getServiceName());
341 EntityManagerFactory emf = null;
342 EntityManager em = null;
344 StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
345 deleteStr.append(getEntityName(ctx));
346 deleteStr.append(" WHERE csid = :csid");
347 //TODO: add tenant id
349 emf = getEntityManagerFactory();
350 em = emf.createEntityManager();
351 Query q = em.createQuery(deleteStr.toString());
352 q.setParameter("csid", id);
353 //TODO: add tenant id
355 em.getTransaction().begin();
356 rcount = q.executeUpdate();
358 if (em != null && em.getTransaction().isActive()) {
359 em.getTransaction().rollback();
361 String msg = "could not find entity with id=" + id;
363 throw new DocumentNotFoundException(msg);
365 em.getTransaction().commit();
367 } catch (DocumentException de) {
369 } catch (Exception e) {
370 if (logger.isDebugEnabled()) {
371 logger.debug("Caught exception ", e);
373 if (em != null && em.getTransaction().isActive()) {
374 em.getTransaction().rollback();
376 throw new DocumentException(e);
379 releaseEntityManagerFactory(emf);
385 * Gets the entity manager factory.
387 * @return the entity manager factory
389 public EntityManagerFactory getEntityManagerFactory() {
390 return getEntityManagerFactory(CS_PERSISTENCE_UNIT);
394 * Gets the entity manager factory.
396 * @param persistenceUnit the persistence unit
398 * @return the entity manager factory
400 public EntityManagerFactory getEntityManagerFactory(
401 String persistenceUnit) {
402 return Persistence.createEntityManagerFactory(persistenceUnit);
407 * Release entity manager factory.
411 public void releaseEntityManagerFactory(EntityManagerFactory emf) {
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
428 protected Object getValue(Object o, String methodName)
429 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
430 if (methodName == null) {
431 String msg = methodName + " cannot be null";
433 throw new IllegalArgumentException(msg);
435 Class c = o.getClass();
436 Method m = c.getMethod(methodName);
438 Object r = m.invoke(o);
439 if (logger.isDebugEnabled()) {
440 logger.debug("getValue returned value=" + r
441 + " for " + c.getName());
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
454 * @throws NoSuchMethodException
455 * @throws IllegalAccessException
456 * @throws InvocationTargetException
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";
463 throw new IllegalArgumentException(msg);
465 if (argType == null) {
466 String msg = "argType cannot be null";
468 throw new IllegalArgumentException(msg);
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());
484 * @param csid the csid
486 * @throws Exception the exception
488 protected void setCsid(Object o, String csid) throws Exception {
490 String id = (String) getValue(o, "getCsid");
492 if (!id.equals(csid)) {
493 String msg = "Csids do not match!";
495 throw new BadRequestException(msg);
503 setValue(o, "setCsid", java.lang.String.class, csid);
507 * Gets the entity name.
511 * @return the entity name
513 protected String getEntityName(ServiceContext ctx) {
514 Object o = ctx.getProperty("entity-name");
516 throw new IllegalArgumentException("property entity-name missing in context "
524 public void get(ServiceContext ctx, DocumentHandler handler)
525 throws DocumentNotFoundException, DocumentException {
526 throw new UnsupportedOperationException();