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.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;
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.
46 * Assumption: each persistent entityReceived has the following 3 attributes
47 <xs:element name="createdAt" type="xs:dateTime">
51 <orm:column name="created_at" nullable="false"/>
56 <xs:element name="updatedAt" type="xs:dateTime">
60 <orm:column name="updated_at" />
66 <xs:attribute name="csid" type="xs:string">
70 <orm:column name="csid" length="128" nullable="false"/>
76 * $LastChangedRevision: $ $LastChangedDate: $
78 public class JpaStorageClientImpl implements StorageClient {
81 private final Logger logger = LoggerFactory.getLogger(JpaStorageClientImpl.class);
84 * Instantiates a new jpa storage client.
86 public JpaStorageClientImpl() {
90 * @see org.collectionspace.services.common.storage.StorageClient#create(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
93 public String create(ServiceContext ctx,
94 DocumentHandler handler) throws BadRequestException,
98 throw new IllegalArgumentException(
99 "JpaStorageClient.create: ctx is missing");
101 if (handler == null) {
102 throw new IllegalArgumentException(
103 "JpaStorageClient.create: handler is missing");
105 EntityManagerFactory emf = null;
106 EntityManager em = null;
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();
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();
125 } catch (DocumentException de) {
126 if (em != null && em.getTransaction().isActive()) {
127 em.getTransaction().rollback();
130 } catch (Exception e) {
131 if (em != null && em.getTransaction().isActive()) {
132 em.getTransaction().rollback();
134 if (logger.isDebugEnabled()) {
135 logger.debug("Caught exception ", e);
137 throw new DocumentException(e);
140 JpaStorageUtils.releaseEntityManagerFactory(emf);
147 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
150 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
151 throws DocumentNotFoundException, DocumentException {
152 throw new UnsupportedOperationException();
156 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
159 public void get(ServiceContext ctx, String id, DocumentHandler handler)
160 throws DocumentNotFoundException, DocumentException {
162 throw new IllegalArgumentException(
163 "JpaStorageClient.get: ctx is missing");
165 if (handler == null) {
166 throw new IllegalArgumentException(
167 "JpaStorageClient.get: handler is missing");
169 DocumentFilter docFilter = handler.getDocumentFilter();
170 if (docFilter == null) {
171 docFilter = handler.createDocumentFilter();
173 EntityManagerFactory emf = null;
174 EntityManager em = null;
176 handler.prepare(Action.GET);
178 o = JpaStorageUtils.getEntity(getEntityName(ctx), id, docFilter);
180 if (em != null && em.getTransaction().isActive()) {
181 em.getTransaction().rollback();
183 String msg = "could not find entity with id=" + id;
184 throw new DocumentNotFoundException(msg);
186 DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(o);
187 handler.handle(Action.GET, wrapDoc);
188 handler.complete(Action.GET, wrapDoc);
189 } catch (DocumentException de) {
191 } catch (Exception e) {
192 if (logger.isDebugEnabled()) {
193 logger.debug("Caught exception ", e);
195 throw new DocumentException(e);
198 JpaStorageUtils.releaseEntityManagerFactory(emf);
204 * @see org.collectionspace.services.common.storage.StorageClient#getAll(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
207 public void getAll(ServiceContext ctx, DocumentHandler handler)
208 throws DocumentNotFoundException, DocumentException {
209 throw new UnsupportedOperationException("use getFiltered instead");
213 * @see org.collectionspace.services.common.storage.StorageClient#getFiltered(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
216 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
217 throws DocumentNotFoundException, DocumentException {
219 throw new IllegalArgumentException(
220 "JpaStorageClient.getFiltered: ctx is missing");
222 if (handler == null) {
223 throw new IllegalArgumentException(
224 "JpaStorageClient.getFiltered: handler is missing");
227 DocumentFilter docFilter = handler.getDocumentFilter();
228 if (docFilter == null) {
229 docFilter = handler.createDocumentFilter();
231 EntityManagerFactory emf = null;
232 EntityManager em = null;
234 handler.prepare(Action.GET_ALL);
236 StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
237 queryStrBldr.append(getEntityName(ctx));
238 queryStrBldr.append(" a");
239 List<DocumentFilter.ParamBinding> params = docFilter.buildWhereForSearch(queryStrBldr);
240 //TODO: add tenant csidReceived
241 emf = JpaStorageUtils.getEntityManagerFactory();
242 em = emf.createEntityManager();
243 String queryStr = queryStrBldr.toString(); //for debugging
244 Query q = em.createQuery(queryStr);
246 for (DocumentFilter.ParamBinding p : params) {
247 q.setParameter(p.getName(), p.getValue());
249 if (docFilter.getOffset() > 0) {
250 q.setFirstResult(docFilter.getOffset());
252 if (docFilter.getPageSize() > 0) {
253 q.setMaxResults(docFilter.getPageSize());
256 //FIXME is transaction required for get?
257 em.getTransaction().begin();
258 List list = q.getResultList();
259 em.getTransaction().commit();
260 DocumentWrapper<List> wrapDoc = new DocumentWrapperImpl<List>(list);
261 handler.handle(Action.GET_ALL, wrapDoc);
262 handler.complete(Action.GET_ALL, wrapDoc);
263 } catch (DocumentException de) {
265 } catch (Exception e) {
266 if (logger.isDebugEnabled()) {
267 logger.debug("Caught exception ", e);
269 throw new DocumentException(e);
272 JpaStorageUtils.releaseEntityManagerFactory(emf);
278 * @see org.collectionspace.services.common.storage.StorageClient#update(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
281 public void update(ServiceContext ctx, String id, DocumentHandler handler)
282 throws BadRequestException, DocumentNotFoundException,
285 throw new IllegalArgumentException(
286 "JpaStorageClient.update: ctx is missing");
288 if (handler == null) {
289 throw new IllegalArgumentException(
290 "JpaStorageClient.update: handler is missing");
292 EntityManagerFactory emf = null;
293 EntityManager em = null;
295 handler.prepare(Action.UPDATE);
296 Object entityReceived = handler.getCommonPart();
297 emf = JpaStorageUtils.getEntityManagerFactory();
298 em = emf.createEntityManager();
299 em.getTransaction().begin();
300 Object entityFound = getEntity(em, id, entityReceived.getClass());
301 DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
302 handler.handle(Action.UPDATE, wrapDoc);
303 JaxbUtils.setValue(entityFound, "setUpdatedAtItem", Date.class, new Date());
304 em.getTransaction().commit();
305 handler.complete(Action.UPDATE, wrapDoc);
306 } catch (BadRequestException bre) {
307 if (em != null && em.getTransaction().isActive()) {
308 em.getTransaction().rollback();
311 } catch (DocumentException de) {
312 if (em != null && em.getTransaction().isActive()) {
313 em.getTransaction().rollback();
316 } catch (Exception e) {
317 if (logger.isDebugEnabled()) {
318 logger.debug("Caught exception ", e);
320 throw new DocumentException(e);
323 JpaStorageUtils.releaseEntityManagerFactory(emf);
329 * delete removes entity and its child entities
330 * cost: a get before delete
331 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
334 public void delete(ServiceContext ctx, String id)
335 throws DocumentNotFoundException,
338 if (logger.isDebugEnabled()) {
339 logger.debug("deleting entity with id=" + id);
343 throw new IllegalArgumentException(
344 "JpaStorageClient.delete: ctx is missing");
346 EntityManagerFactory emf = null;
347 EntityManager em = null;
350 emf = JpaStorageUtils.getEntityManagerFactory();
351 em = emf.createEntityManager();
353 em.getTransaction().begin();
354 Object entityFound = getEntity(ctx, em, id);
355 if (entityFound == null) {
356 if (em != null && em.getTransaction().isActive()) {
357 em.getTransaction().rollback();
359 String msg = "could not find entity with id=" + id;
361 throw new DocumentNotFoundException(msg);
363 em.remove(entityFound);
364 em.getTransaction().commit();
366 } catch (DocumentException de) {
367 if (em != null && em.getTransaction().isActive()) {
368 em.getTransaction().rollback();
371 } catch (Exception e) {
372 if (logger.isDebugEnabled()) {
373 logger.debug("Caught exception ", e);
375 if (em != null && em.getTransaction().isActive()) {
376 em.getTransaction().rollback();
378 throw new DocumentException(e);
381 JpaStorageUtils.releaseEntityManagerFactory(emf);
387 * deleteWhere uses the where clause to delete an entityReceived represented by the csidReceived
388 * it does not delete any child entities.
391 * @throws DocumentNotFoundException
392 * @throws DocumentException
394 public void deleteWhere(ServiceContext ctx, String id)
395 throws DocumentNotFoundException,
399 throw new IllegalArgumentException(
400 "JpaStorageClient.deleteWhere: ctx is missing");
403 if (logger.isDebugEnabled()) {
404 logger.debug("deleting entity with id=" + id);
406 EntityManagerFactory emf = null;
407 EntityManager em = null;
409 StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
410 deleteStr.append(getEntityName(ctx));
411 deleteStr.append(" WHERE csid = :csid");
412 //TODO: add tenant csidReceived
414 emf = JpaStorageUtils.getEntityManagerFactory();
415 em = emf.createEntityManager();
416 Query q = em.createQuery(deleteStr.toString());
417 q.setParameter("csid", id);
420 em.getTransaction().begin();
421 rcount = q.executeUpdate();
423 if (em != null && em.getTransaction().isActive()) {
424 em.getTransaction().rollback();
426 String msg = "could not find entity with id=" + id;
428 throw new DocumentNotFoundException(msg);
430 em.getTransaction().commit();
432 } catch (DocumentException de) {
433 if (em != null && em.getTransaction().isActive()) {
434 em.getTransaction().rollback();
437 } catch (Exception e) {
438 if (logger.isDebugEnabled()) {
439 logger.debug("Caught exception ", e);
441 if (em != null && em.getTransaction().isActive()) {
442 em.getTransaction().rollback();
444 throw new DocumentException(e);
447 JpaStorageUtils.releaseEntityManagerFactory(emf);
453 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
454 throws DocumentNotFoundException, DocumentException {
456 throw new IllegalArgumentException(
457 "JpaStorageClient.delete: ctx is missing");
459 if (handler == null) {
460 throw new IllegalArgumentException(
461 "JpaStorageClient.delete: handler is missing");
463 EntityManagerFactory emf = null;
464 EntityManager em = null;
466 handler.prepare(Action.DELETE);
467 Object entity = handler.getCommonPart();
468 DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entity);
470 handler.handle(Action.DELETE, wrapDoc);
471 handler.complete(Action.DELETE, wrapDoc);
472 } catch (DocumentException de) {
473 if (em != null && em.getTransaction().isActive()) {
474 em.getTransaction().rollback();
477 } catch (Exception e) {
478 if (logger.isDebugEnabled()) {
479 logger.debug("Caught exception ", e);
481 if (em != null && em.getTransaction().isActive()) {
482 em.getTransaction().rollback();
484 throw new DocumentException(e);
487 JpaStorageUtils.releaseEntityManagerFactory(emf);
493 * Gets the entityReceived name.
497 * @return the entityReceived name
499 protected String getEntityName(ServiceContext ctx) {
500 Object o = ctx.getProperty(ServiceContextProperties.ENTITY_NAME);
502 throw new IllegalArgumentException(ServiceContextProperties.ENTITY_NAME
503 + "property is missing in context "
511 * getEntity returns persistent entity for given id. it assumes that
512 * service context has property ServiceContextProperties.ENTITY_CLASS set
513 * rolls back the transaction if not found
514 * @param ctx service context
515 * @param em entity manager
516 * @param csid received
518 * @throws DocumentNotFoundException and rollsback the transaction if active
520 protected Object getEntity(ServiceContext ctx, EntityManager em, String id)
521 throws DocumentNotFoundException {
522 Class entityClazz = (Class) ctx.getProperty(ServiceContextProperties.ENTITY_CLASS);
523 if (entityClazz == null) {
524 String msg = ServiceContextProperties.ENTITY_CLASS
525 + " property is missing in the context";
527 throw new IllegalArgumentException(msg);
529 return getEntity(em, id, entityClazz);
533 * getEntity retrieves the persistent entity of given class for given id
534 * rolls back the transaction if not found
536 * @param id entity id
539 * @throws DocumentNotFoundException and rollsback the transaction if active
541 protected Object getEntity(EntityManager em, String id, Class entityClazz)
542 throws DocumentNotFoundException {
543 Object entityFound = JpaStorageUtils.getEntity(em, id, entityClazz);
544 if (entityFound == null) {
545 if (em != null && em.getTransaction().isActive()) {
546 em.getTransaction().rollback();
548 String msg = "could not find entity of type=" + entityClazz.getName()
551 throw new DocumentNotFoundException(msg);
557 public void get(ServiceContext ctx, DocumentHandler handler)
558 throws DocumentNotFoundException, DocumentException {
559 throw new UnsupportedOperationException();