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 2010 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 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 package org.collectionspace.services.common.storage.jpa;
26 import org.collectionspace.services.common.context.ServiceContextProperties;
28 import java.util.ArrayList;
29 import java.util.Date;
30 import java.util.List;
31 import java.util.UUID;
33 import javax.persistence.EntityManager;
34 import javax.persistence.EntityManagerFactory;
35 import javax.persistence.NoResultException;
36 import javax.persistence.PersistenceException;
37 import javax.persistence.Query;
39 import org.collectionspace.services.authorization.perms.Permission;
40 import org.collectionspace.services.authorization.PermissionValue;
41 import org.collectionspace.services.authorization.Role;
42 import org.collectionspace.services.authorization.RoleValue;
43 import org.collectionspace.services.authorization.AccountRoleRel;
44 import org.collectionspace.services.authorization.PermissionRoleRel;
46 import org.collectionspace.services.common.context.ServiceContext;
47 import org.collectionspace.services.common.document.BadRequestException;
48 import org.collectionspace.services.common.document.DocumentException;
49 import org.collectionspace.services.common.document.DocumentFilter;
50 import org.collectionspace.services.common.document.DocumentHandler;
51 import org.collectionspace.services.common.document.DocumentHandler.Action;
52 import org.collectionspace.services.common.document.DocumentNotFoundException;
53 import org.collectionspace.services.common.document.DocumentWrapper;
54 import org.collectionspace.services.common.document.DocumentWrapperImpl;
55 import org.collectionspace.services.common.document.JaxbUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
61 * JpaRelationshipStorageClient deals with a relationship
62 * in persistent storage. This storage client deals with bulk operations, i.e.
63 * create/post inserts multiple tuples between the given object and subjects
64 * get retrieves all subjects for the given object in relationship
65 * delete deletes all subjects for the given object in relationship
68 public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
70 private final Logger logger = LoggerFactory.getLogger(JpaRelationshipStorageClient.class);
72 public static PermissionValue createPermissionValue(Permission permission) {
73 PermissionValue result = new PermissionValue();
74 result.setPermissionId(permission.getCsid());
75 result.setResourceName(permission.getResourceName());
76 result.setActionGroup(permission.getActionGroup());
80 public static RoleValue createRoleValue(Role role) {
81 RoleValue result = new RoleValue();
82 result.setRoleId(role.getCsid());
83 result.setRoleName(role.getRoleName());
87 public JpaRelationshipStorageClient() {
92 * create of a relationship creates one or more relationships between
94 * the object and subjects of the relationship is chosen (by doc handler) from
99 * @throws BadRequestException
100 * @throws DocumentException
103 public String create(ServiceContext ctx,
104 DocumentHandler handler) throws BadRequestException,
108 throw new IllegalArgumentException(
109 "create : ctx is missing");
111 if (handler == null) {
112 throw new IllegalArgumentException(
113 "create: handler is missing");
115 EntityManagerFactory emf = null;
116 EntityManager em = null;
118 handler.prepare(Action.CREATE);
119 List<T> rl = new ArrayList<T>();
120 DocumentWrapper<List<T>> wrapDoc =
121 new DocumentWrapperImpl<List<T>>(rl);
122 handler.handle(Action.CREATE, wrapDoc);
123 emf = JpaStorageUtils.getEntityManagerFactory();
124 em = emf.createEntityManager();
125 em.getTransaction().begin();
127 JaxbUtils.setValue(r, "setCreatedAtItem", Date.class, new Date());
130 em.getTransaction().commit();
131 handler.complete(Action.CREATE, wrapDoc);
132 return UUID.randomUUID().toString(); //filler, not useful
133 } catch (BadRequestException bre) {
134 if (em != null && em.getTransaction().isActive()) {
135 em.getTransaction().rollback();
138 } catch (PersistenceException pe) {
140 } catch (Exception e) {
141 if (em != null && em.getTransaction().isActive()) {
142 em.getTransaction().rollback();
144 if (logger.isDebugEnabled()) {
145 logger.debug("Caught exception ", e);
147 throw new DocumentException(e);
150 JpaStorageUtils.releaseEntityManagerFactory(emf);
156 * get retrieves all relationships for the object in the relationship
157 * identified by the id. the object could be a permission or a role
159 * @param id of the object in the relationship
161 * @throws DocumentNotFoundException
162 * @throws DocumentException
165 public void get(ServiceContext ctx, String id, DocumentHandler handler)
166 throws DocumentNotFoundException, DocumentException {
168 throw new IllegalArgumentException(
169 "get: ctx is missing");
171 if (handler == null) {
172 throw new IllegalArgumentException(
173 "get: handler is missing");
175 if (getObject(ctx, id) == null) {
177 + "could not find the object with id=" + id;
179 throw new DocumentNotFoundException(msg);
181 String objectId = getObjectId(ctx);
182 if (logger.isDebugEnabled()) {
183 logger.debug("get: using objectId=" + objectId);
185 Class objectClass = getObjectClass(ctx);
186 if (logger.isDebugEnabled()) {
187 logger.debug("get: using object class=" + objectClass.getName());
189 DocumentFilter docFilter = handler.getDocumentFilter();
190 if (docFilter == null) {
191 docFilter = handler.createDocumentFilter();
193 EntityManagerFactory emf = null;
194 EntityManager em = null;
196 handler.prepare(Action.GET);
197 StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
198 queryStrBldr.append(getEntityName(ctx));
199 queryStrBldr.append(" a");
201 queryStrBldr.append(" WHERE " + objectId + " = :objectId");
202 String where = docFilter.getWhereClause();
203 if ((null != where) && (where.length() > 0)) {
204 queryStrBldr.append(" AND " + where);
206 emf = JpaStorageUtils.getEntityManagerFactory();
207 em = emf.createEntityManager();
208 String queryStr = queryStrBldr.toString(); //for debugging
209 if (logger.isDebugEnabled()) {
210 logger.debug("get: jql=" + queryStr.toString());
212 Query q = em.createQuery(queryStr);
213 q.setParameter("objectId", id);
215 List<T> rl = new ArrayList<T>();
217 //require transaction for get?
218 em.getTransaction().begin();
219 rl = q.getResultList();
220 em.getTransaction().commit();
221 } catch (NoResultException nre) {
222 if (em != null && em.getTransaction().isActive()) {
223 em.getTransaction().rollback();
225 String msg = "get(1): "
226 + " could not find relationships for object class="
227 + objectClass.getName() + " id=" + id;
228 if (logger.isDebugEnabled()) {
229 logger.debug(msg, nre);
232 if (rl.size() == 0) {
233 String msg = "get(2): "
234 + " could not find relationships for object class="
235 + objectClass.getName() + " id=" + id;
236 if (logger.isDebugEnabled()) {
240 DocumentWrapper<List<T>> wrapDoc =
241 new DocumentWrapperImpl<List<T>>(rl);
242 handler.handle(Action.GET, wrapDoc);
243 handler.complete(Action.GET, wrapDoc);
244 } catch (DocumentException de) {
246 } catch (Exception e) {
247 if (logger.isDebugEnabled()) {
248 logger.debug("Caught exception ", e);
250 throw new DocumentException(e);
253 JpaStorageUtils.releaseEntityManagerFactory(emf);
261 * @param relationship the relationship
264 private Long getId(T relationship) {
267 if (relationship != null) {
268 if (relationship instanceof AccountRoleRel) {
269 AccountRoleRel accountRoleRel = (AccountRoleRel)relationship;
270 result = accountRoleRel.getHjid();
271 } else if (relationship instanceof PermissionRoleRel) {
272 PermissionRoleRel permissionRoleRel = (PermissionRoleRel)relationship;
273 result = permissionRoleRel.getHjid();
281 * Gets the relationship.
284 * @param relationship the relationship
285 * @return the relationship
286 * @throws DocumentNotFoundException the document not found exception
288 private T getRelationship(EntityManager em, T relationship)
289 throws DocumentNotFoundException {
290 Long id = getId(relationship);
292 T relationshipFound = (T)em.find(relationship.getClass(), id);
293 if (relationshipFound == null) {
294 String msg = "Could not find relationship with id=" + id;
295 if (logger.isErrorEnabled() == true) {
298 throw new DocumentNotFoundException(msg);
300 return relationshipFound;
304 * delete removes all the relationships for the object in the relationship
305 * identified by the id. the object could be a permission or a role
307 * @param id of the object in the relationship
308 * @throws DocumentNotFoundException
309 * @throws DocumentException
312 public void delete(ServiceContext ctx, String id)
313 throws DocumentNotFoundException,
317 throw new IllegalArgumentException(
318 "delete : ctx is missing");
320 if (getObject(ctx, id) == null) {
321 String msg = "delete : "
322 + "could not find the object with id=" + id;
324 throw new DocumentNotFoundException(msg);
326 String objectId = getObjectId(ctx);
327 if (logger.isDebugEnabled()) {
328 logger.debug("delete: using objectId=" + objectId);
330 Class objectClass = getObjectClass(ctx);
331 if (logger.isDebugEnabled()) {
332 logger.debug("delete: using object class=" + objectClass.getName());
334 EntityManagerFactory emf = null;
335 EntityManager em = null;
337 StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
338 String entityName = getEntityName(ctx);
339 deleteStr.append(entityName);
340 deleteStr.append(" WHERE " + objectId + " = :objectId");
341 emf = JpaStorageUtils.getEntityManagerFactory();
342 em = emf.createEntityManager();
343 if (logger.isDebugEnabled()) {
344 logger.debug("delete: jql=" + deleteStr.toString());
346 Query q = em.createQuery(deleteStr.toString());
347 q.setParameter("objectId", id);
349 em.getTransaction().begin();
350 if (logger.isDebugEnabled() == true) {
351 logger.debug(q.toString());
353 rcount = q.executeUpdate();
354 if (logger.isDebugEnabled()) {
355 logger.debug("deleted " + rcount + " relationships for entity " + entityName
356 + " with objectId=" + objectId);
358 em.getTransaction().commit();
360 } catch (Exception e) {
361 if (logger.isDebugEnabled()) {
362 logger.debug("Caught exception ", e);
364 if (em != null && em.getTransaction().isActive()) {
365 em.getTransaction().rollback();
367 throw new DocumentException(e);
370 JpaStorageUtils.releaseEntityManagerFactory(emf);
376 * delete of a relationship deletes one or more relationships between
377 * permission and role
378 * the object and subjects of the relationship is chosen (by doc handler) from
383 * @throws DocumentNotFoundException
384 * @throws DocumentException
387 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
388 throws DocumentNotFoundException, DocumentException {
391 throw new IllegalArgumentException(
392 "delete : ctx is missing");
394 if (handler == null) {
395 throw new IllegalArgumentException(
396 "delete : handler is missing");
398 EntityManagerFactory emf = null;
399 EntityManager em = null;
401 handler.prepare(Action.DELETE);
402 List<T> rl = new ArrayList<T>();
403 DocumentWrapper<List<T>> wrapDoc =
404 new DocumentWrapperImpl<List<T>>(rl);
405 handler.handle(Action.DELETE, wrapDoc);
406 emf = JpaStorageUtils.getEntityManagerFactory();
407 em = emf.createEntityManager();
408 em.getTransaction().begin();
409 //the following could be much more efficient if done with a single
412 em.remove(getRelationship(em, r));
414 em.getTransaction().commit();
415 handler.complete(Action.DELETE, wrapDoc);
416 } catch (DocumentException de) {
417 if (em != null && em.getTransaction().isActive()) {
418 em.getTransaction().rollback();
421 } catch (Exception e) {
422 if (logger.isDebugEnabled()) {
423 logger.debug("delete(ctx, ix, handler): Caught exception ", e);
425 if (em != null && em.getTransaction().isActive()) {
426 em.getTransaction().rollback();
428 throw new DocumentException(e);
431 JpaStorageUtils.releaseEntityManagerFactory(emf);
437 * getObjectId returns the id of the object in a relationship
441 protected String getObjectId(ServiceContext ctx) {
442 String objectId = (String) ctx.getProperty(ServiceContextProperties.OBJECT_ID);
443 if (objectId == null) {
444 String msg = ServiceContextProperties.OBJECT_ID
445 + " property is missing in the context";
447 throw new IllegalArgumentException(msg);
454 * getObjectClass returns the class of the object in a relationship
458 protected Class getObjectClass(ServiceContext ctx) {
459 Class objectClass = (Class) ctx.getProperty(ServiceContextProperties.OBJECT_CLASS);
460 if (objectClass == null) {
461 String msg = ServiceContextProperties.OBJECT_CLASS
462 + " property is missing in the context";
464 throw new IllegalArgumentException(msg);
470 * getObject returns the object in the relationship
475 protected Object getObject(ServiceContext ctx, String id)
476 throws DocumentNotFoundException {
477 Class objectClass = getObjectClass(ctx);
478 return JpaStorageUtils.getEntity(id, objectClass);