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;
32 import javax.persistence.NoResultException;
33 import javax.persistence.PersistenceException;
34 import javax.persistence.Query;
36 import org.collectionspace.services.authorization.perms.Permission;
37 import org.collectionspace.services.authorization.PermissionValue;
38 import org.collectionspace.services.authorization.Role;
39 import org.collectionspace.services.authorization.RoleValue;
40 import org.collectionspace.services.authorization.AccountRoleRel;
41 import org.collectionspace.services.authorization.PermissionRoleRel;
43 import org.collectionspace.services.common.context.ServiceContext;
44 import org.collectionspace.services.common.document.BadRequestException;
45 import org.collectionspace.services.common.document.DocumentException;
46 import org.collectionspace.services.common.document.DocumentFilter;
47 import org.collectionspace.services.common.document.DocumentHandler;
48 import org.collectionspace.services.common.document.DocumentHandler.Action;
49 import org.collectionspace.services.common.document.DocumentNotFoundException;
50 import org.collectionspace.services.common.document.DocumentWrapper;
51 import org.collectionspace.services.common.document.DocumentWrapperImpl;
52 import org.collectionspace.services.common.document.JaxbUtils;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 * JpaRelationshipStorageClient deals with a relationship
59 * in persistent storage. This storage client deals with bulk operations, i.e.
60 * create/post inserts multiple tuples between the given object and subjects
61 * get retrieves all subjects for the given object in relationship
62 * delete deletes all subjects for the given object in relationship
65 @SuppressWarnings({ "rawtypes", "unchecked" })
66 public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
68 private final Logger logger = LoggerFactory.getLogger(JpaRelationshipStorageClient.class);
70 public static PermissionValue createPermissionValue(Permission permission) {
71 PermissionValue result = new PermissionValue();
72 result.setPermissionId(permission.getCsid());
73 result.setResourceName(permission.getResourceName());
74 result.setActionGroup(permission.getActionGroup());
78 public static RoleValue createRoleValue(Role role) {
79 RoleValue result = new RoleValue();
80 result.setRoleId(role.getCsid());
81 result.setRoleName(role.getRoleName());
85 public JpaRelationshipStorageClient() {
90 * create of a relationship creates one or more relationships between
92 * the object and subjects of the relationship is chosen (by doc handler) from
97 * @throws BadRequestException
98 * @throws DocumentException
101 public String create(ServiceContext ctx,
102 DocumentHandler handler) throws BadRequestException,
104 String result = null;
106 JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();
108 jpaTransactionContext.beginTransaction();
109 handler.prepare(Action.CREATE);
110 List<T> rl = new ArrayList<T>();
111 DocumentWrapper<List<T>> wrapDoc =
112 new DocumentWrapperImpl<List<T>>(rl);
113 handler.handle(Action.CREATE, wrapDoc);
115 JaxbUtils.setValue(r, "setCreatedAtItem", Date.class, new Date());
116 jpaTransactionContext.persist(r);
118 handler.complete(Action.CREATE, wrapDoc);
119 jpaTransactionContext.commitTransaction();
120 result = "-1"; // meaningless result
121 } catch (BadRequestException bre) {
122 jpaTransactionContext.markForRollback();
124 } catch (PersistenceException pe) {
125 jpaTransactionContext.markForRollback();
127 } catch (Exception e) {
128 jpaTransactionContext.markForRollback();
129 if (logger.isDebugEnabled()) {
130 logger.debug("Caught exception ", e);
132 throw new DocumentException(e);
134 ctx.closeConnection();
141 * get retrieves all relationships for the object in the relationship
142 * identified by the id. the object could be a permission or a role
144 * @param id of the object in the relationship
146 * @throws DocumentNotFoundException
147 * @throws DocumentException
150 public void get(ServiceContext ctx, String id, DocumentHandler handler)
151 throws DocumentNotFoundException, DocumentException {
153 if (getObject(ctx, id) == null) {
155 + "could not find the object with id=" + id;
157 throw new DocumentNotFoundException(msg);
160 String objectId = getObjectId(ctx);
161 if (logger.isDebugEnabled()) {
162 logger.debug("get: using objectId=" + objectId);
164 Class objectClass = getObjectClass(ctx);
165 if (logger.isDebugEnabled()) {
166 logger.debug("get: using object class=" + objectClass.getName());
168 DocumentFilter docFilter = handler.getDocumentFilter();
169 if (docFilter == null) {
170 docFilter = handler.createDocumentFilter();
173 JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection();
175 handler.prepare(Action.GET);
176 StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
177 queryStrBldr.append(getEntityName(ctx));
178 queryStrBldr.append(" a");
180 queryStrBldr.append(" WHERE " + objectId + " = :objectId");
181 String where = docFilter.getWhereClause();
182 if ((null != where) && (where.length() > 0)) {
183 queryStrBldr.append(" AND " + where);
186 String queryStr = queryStrBldr.toString(); //for debugging
187 if (logger.isDebugEnabled()) {
188 logger.debug("get: jql=" + queryStr.toString());
190 Query q = jpaConnectionContext.createQuery(queryStr);
191 q.setParameter("objectId", id);
193 List<T> rl = new ArrayList<T>();
194 jpaConnectionContext.beginTransaction();
196 rl = q.getResultList();
197 } catch (NoResultException nre) {
198 String msg = "get(1): " + " could not find relationships for object class="
199 + objectClass.getName() + " id=" + id;
200 if (logger.isDebugEnabled()) {
201 logger.debug(msg, nre);
204 if (rl.size() == 0) {
205 String msg = "get(2): " + " could not find relationships for object class="
206 + objectClass.getName() + " id=" + id;
207 if (logger.isDebugEnabled()) {
211 DocumentWrapper<List<T>> wrapDoc =
212 new DocumentWrapperImpl<List<T>>(rl);
213 handler.handle(Action.GET, wrapDoc);
214 handler.complete(Action.GET, wrapDoc);
215 jpaConnectionContext.commitTransaction();
216 } catch (DocumentException de) {
217 jpaConnectionContext.markForRollback();
218 } catch (Exception e) {
219 jpaConnectionContext.markForRollback();
220 if (logger.isDebugEnabled()) {
221 logger.debug("Caught exception ", e);
223 throw new DocumentException(e);
225 ctx.closeConnection();
232 * @param relationship the relationship
235 private Long getId(T relationship) {
238 if (relationship != null) {
239 if (relationship instanceof AccountRoleRel) {
240 AccountRoleRel accountRoleRel = (AccountRoleRel)relationship;
241 result = accountRoleRel.getHjid();
242 } else if (relationship instanceof PermissionRoleRel) {
243 PermissionRoleRel permissionRoleRel = (PermissionRoleRel)relationship;
244 result = permissionRoleRel.getHjid();
252 * Gets the relationship.
255 * @param relationship the relationship
256 * @return the relationship
257 * @throws DocumentNotFoundException the document not found exception
259 private T getRelationship(JPATransactionContext jpaTransactionContext, T relationship)
260 throws DocumentNotFoundException {
261 Long id = getId(relationship);
263 T relationshipFound = (T)jpaTransactionContext.find(relationship.getClass(), id);
264 if (relationshipFound == null) {
265 String msg = "Could not find relationship with id=" + id;
266 if (logger.isErrorEnabled() == true) {
269 throw new DocumentNotFoundException(msg);
272 return relationshipFound;
276 * delete removes all the relationships for the object in the relationship
277 * identified by the id. the object could be a permission or a role
279 * @param id of the object in the relationship
280 * @throws DocumentNotFoundException
281 * @throws DocumentException
284 public void delete(ServiceContext ctx, String id)
285 throws DocumentNotFoundException,
288 if (getObject(ctx, id) == null) {
289 String msg = "delete : " + "could not find the object with id=" + id;
291 throw new DocumentNotFoundException(msg);
294 String objectId = getObjectId(ctx);
295 if (logger.isDebugEnabled()) {
296 logger.debug("delete: using objectId=" + objectId);
298 Class objectClass = getObjectClass(ctx);
299 if (logger.isDebugEnabled()) {
300 logger.debug("delete: using object class=" + objectClass.getName());
303 JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection();
305 StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
306 String entityName = getEntityName(ctx);
307 deleteStr.append(entityName);
308 deleteStr.append(" WHERE " + objectId + " = :objectId");
309 if (logger.isDebugEnabled()) {
310 logger.debug("delete: jql=" + deleteStr.toString());
312 Query q = jpaConnectionContext.createQuery(deleteStr.toString());
313 q.setParameter("objectId", id);
315 jpaConnectionContext.beginTransaction();
316 if (logger.isDebugEnabled() == true) {
317 logger.debug(q.toString());
319 rcount = q.executeUpdate();
320 if (logger.isDebugEnabled()) {
321 logger.debug("deleted " + rcount + " relationships for entity " + entityName
322 + " with objectId=" + objectId);
324 jpaConnectionContext.commitTransaction();
325 } catch (Exception e) {
326 jpaConnectionContext.markForRollback();
327 if (logger.isDebugEnabled()) {
328 logger.debug("Caught exception ", e);
330 throw new DocumentException(e);
332 ctx.closeConnection();
337 * delete of a relationship deletes one or more relationships between
338 * permission and role
339 * the object and subjects of the relationship is chosen (by doc handler) from
344 * @throws DocumentNotFoundException
345 * @throws DocumentException
348 public boolean delete(ServiceContext ctx, String id, DocumentHandler handler)
349 throws DocumentNotFoundException, DocumentException {
350 boolean result = true;
352 JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();
354 jpaTransactionContext.beginTransaction();
355 handler.prepare(Action.DELETE);
356 List<T> rl = new ArrayList<T>();
357 DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(rl);
358 handler.handle(Action.DELETE, wrapDoc);
360 //the following could be much more efficient if done with a single sql/jql
363 jpaTransactionContext.remove(getRelationship(jpaTransactionContext, r));
365 handler.complete(Action.DELETE, wrapDoc); // Delete from the Spring Security tables. Would be better if this was part of the earlier transaction.
366 jpaTransactionContext.commitTransaction();
367 } catch (DocumentException de) {
368 jpaTransactionContext.markForRollback();
370 } catch (Exception e) {
371 jpaTransactionContext.markForRollback();
372 if (logger.isDebugEnabled()) {
373 logger.debug("delete(ctx, ix, handler): Caught exception ", e);
375 throw new DocumentException(e);
377 ctx.closeConnection();
384 * getObjectId returns the id of the object in a relationship
388 protected String getObjectId(ServiceContext ctx) {
389 String objectId = (String) ctx.getProperty(ServiceContextProperties.OBJECT_ID);
391 if (objectId == null) {
392 String msg = ServiceContextProperties.OBJECT_ID + " property is missing in the context";
394 throw new IllegalArgumentException(msg);
401 * getObjectClass returns the class of the object in a relationship
405 protected Class getObjectClass(ServiceContext ctx) {
406 Class objectClass = (Class) ctx.getProperty(ServiceContextProperties.OBJECT_CLASS);
408 if (objectClass == null) {
409 String msg = ServiceContextProperties.OBJECT_CLASS + " property is missing in the context";
411 throw new IllegalArgumentException(msg);
418 * getObject returns the object in the relationship
423 protected Object getObject(ServiceContext ctx, String id)
424 throws DocumentNotFoundException {
425 Class objectClass = getObjectClass(ctx);
426 return JpaStorageUtils.getEntity((JPATransactionContext)ctx.getCurrentTransactionContext(), id, objectClass);