]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
d2a92aa74f4076e0e7f1dbfe339bf169ba794e6e
[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 2010 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  *  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.
23  */
24 package org.collectionspace.services.common.storage.jpa;
25
26 import org.collectionspace.services.common.context.ServiceContextProperties;
27
28 import java.util.ArrayList;
29 import java.util.Date;
30 import java.util.List;
31
32 import javax.persistence.NoResultException;
33 import javax.persistence.PersistenceException;
34 import javax.persistence.Query;
35
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;
42 import org.collectionspace.services.common.api.Tools;
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;
53 import org.hsqldb.lib.StringUtil;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
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
63  * @author 
64  */
65 @SuppressWarnings({ "rawtypes", "unchecked" })
66 public class JpaRelationshipStorageClient<T> extends JpaStorageClientImpl {
67
68     private final Logger logger = LoggerFactory.getLogger(JpaRelationshipStorageClient.class);
69     
70     public JpaRelationshipStorageClient() {
71         //empty
72     }
73
74     /**
75      * create of a relationship creates one or more relationships between
76      * permission and role
77      * the object and subjects of the relationship is chosen (by doc handler) from
78      * the payload
79      * @param ctx
80      * @param handler
81      * @return
82      * @throws BadRequestException
83      * @throws DocumentException
84      */
85         @Override
86     public String create(ServiceContext ctx,
87             DocumentHandler handler) throws BadRequestException,
88             DocumentException {
89         String result = null;
90         
91         JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();      
92         try {
93             jpaTransactionContext.beginTransaction();
94             handler.prepare(Action.CREATE);
95             List<T> relationshipList = new ArrayList<T>();
96             DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(relationshipList);
97             handler.handle(Action.CREATE, wrapDoc);
98             for (T relationship : relationshipList) {
99                 JaxbUtils.setValue(relationship, "setCreatedAtItem", Date.class, new Date());
100                 jpaTransactionContext.persist(relationship);
101             }
102             handler.complete(Action.CREATE, wrapDoc); 
103             jpaTransactionContext.commitTransaction();
104             result = "0"; // meaningless result
105         } catch (BadRequestException bre) {
106             throw bre;
107         } catch (PersistenceException pe) {
108                 throw pe;
109         } catch (DocumentException de) {
110                 throw de;
111         } catch (Exception e) {
112             throw new DocumentException(e);
113         } finally {
114                 if (result == null) {
115                 jpaTransactionContext.markForRollback();  // If result == null, we failed and must mark the current tx for rollback
116                 }
117             ctx.closeConnection();
118         }
119         
120         return result;
121     }
122
123     /**
124      * get retrieves all relationships for the object in the relationship
125      * identified by the id. the object could be a permission or a role
126      * @param ctx
127      * @param id of the object in the relationship
128      * @param handler
129      * @throws DocumentNotFoundException
130      * @throws DocumentException
131      */
132     @Override
133     public void get(ServiceContext ctx, String id, DocumentHandler handler)
134             throws DocumentNotFoundException, DocumentException {
135
136         JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection();
137         try {
138                 if (getObject(ctx, id) == null) {
139                     String msg = "get: " + "could not find the object with id=" + id;
140                     logger.error(msg);
141                     throw new DocumentNotFoundException(msg);
142                 }
143                 
144                 String objectId = getObjectId(ctx);
145                 DocumentFilter docFilter = handler.getDocumentFilter();
146                 if (docFilter == null) {
147                     docFilter = handler.createDocumentFilter();
148                 }
149         
150             handler.prepare(Action.GET);
151             StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
152             queryStrBldr.append(getEntityName(ctx));
153             queryStrBldr.append(" a");
154             
155             String joinFetch = docFilter.getJoinFetchClause();
156             if (Tools.notBlank(joinFetch)) {
157                 queryStrBldr.append(" " + joinFetch);
158             }
159
160             queryStrBldr.append(" WHERE " + objectId + " = :objectId");
161             String where = docFilter.getWhereClause();
162             if ((null != where) && (where.length() > 0)) {
163                 queryStrBldr.append(" AND " + where);
164             }
165
166             String queryStr = queryStrBldr.toString(); //for debugging
167             if (logger.isDebugEnabled()) {
168                 logger.debug("get: jql=" + queryStr.toString());
169             }
170             Query q = jpaConnectionContext.createQuery(queryStr);
171             q.setParameter("objectId", id);
172
173             List<T> relList = new ArrayList<T>();
174                 jpaConnectionContext.beginTransaction();
175             try {
176                 relList = q.getResultList();
177             } catch (NoResultException nre) {
178                 // Quietly consume.  relList will just be an empty list
179             }
180
181             DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(relList);
182             handler.handle(Action.GET, wrapDoc);
183             handler.complete(Action.GET, wrapDoc);
184             jpaConnectionContext.commitTransaction();
185         } catch (DocumentNotFoundException nfe) {
186                 jpaConnectionContext.markForRollback();
187                 throw nfe;
188         } catch (DocumentException de) {
189                 jpaConnectionContext.markForRollback();
190                 throw de;
191         } catch (Exception e) {
192                 jpaConnectionContext.markForRollback();
193             if (logger.isDebugEnabled()) {
194                 logger.debug("Caught exception ", e);
195             }
196             throw new DocumentException(e);
197         } finally {
198             ctx.closeConnection();
199         }
200     }
201     
202     /**
203      * Gets the id.
204      *
205      * @param relationship the relationship
206      * @return the id
207      */
208     private Long getId(T relationship) {
209         Long result = null;
210         
211         if (relationship != null) {
212                 if (relationship instanceof AccountRoleRel) {
213                         AccountRoleRel accountRoleRel = (AccountRoleRel)relationship;
214                         result = accountRoleRel.getHjid();
215                 } else if (relationship instanceof PermissionRoleRel) {
216                         PermissionRoleRel permissionRoleRel = (PermissionRoleRel)relationship;
217                         result = permissionRoleRel.getHjid();
218                 }
219         }
220         
221         return result;
222     }
223     
224     /**
225      * Gets the relationship.
226      *
227      * @param em the em
228      * @param relationship the relationship
229      * @return the relationship
230      * @throws DocumentNotFoundException the document not found exception
231      */
232     private T getRelationship(JPATransactionContext jpaTransactionContext, T relationship)
233                 throws DocumentNotFoundException {
234         Long id = getId(relationship);
235         
236         T relationshipFound = (T)jpaTransactionContext.find(relationship.getClass(), id);
237         if (relationshipFound == null) {
238             String msg = "Could not find relationship with id=" + id;
239             if (logger.isErrorEnabled() == true) {
240                 logger.error(msg);
241             }
242             throw new DocumentNotFoundException(msg);
243         }
244         
245         return relationshipFound;
246     }
247
248     /**
249      * delete removes all the relationships for the object in the relationship
250      * identified by the id. the object could be a permission or a role
251      * @param ctx
252      * @param id of the object in the relationship
253      * @throws DocumentNotFoundException
254      * @throws DocumentException
255      */
256     @Override
257     public void delete(ServiceContext ctx, String id)
258             throws DocumentNotFoundException,
259             DocumentException {
260
261         if (getObject(ctx, id) == null) {
262             String msg = "delete : " + "could not find the object with id=" + id;
263             logger.error(msg);
264             throw new DocumentNotFoundException(msg);
265         }
266         
267         String objectId = getObjectId(ctx);
268         if (logger.isDebugEnabled()) {
269             logger.debug("delete: using objectId=" + objectId);
270         }
271         Class objectClass = getObjectClass(ctx);
272         if (logger.isDebugEnabled()) {
273             logger.debug("delete: using object class=" + objectClass.getName());
274         }
275         
276         JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection();       
277         try {
278             StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
279             String entityName = getEntityName(ctx);
280             deleteStr.append(entityName);
281             deleteStr.append(" WHERE " + objectId + " = :objectId");
282             if (logger.isDebugEnabled()) {
283                 logger.debug("delete: jql=" + deleteStr.toString());
284             }
285             Query q = jpaConnectionContext.createQuery(deleteStr.toString());
286             q.setParameter("objectId", id);
287             int rcount = 0;
288             jpaConnectionContext.beginTransaction();
289             if (logger.isDebugEnabled() == true) {
290                 logger.debug(q.toString());
291             }
292             rcount = q.executeUpdate();
293             if (logger.isDebugEnabled()) {
294                 logger.debug("deleted " + rcount + " relationships for entity " + entityName
295                         + " with objectId=" + objectId);
296             }
297             jpaConnectionContext.commitTransaction();
298         } catch (Exception e) {
299                 jpaConnectionContext.markForRollback();
300             if (logger.isDebugEnabled()) {
301                 logger.debug("Caught exception ", e);
302             }
303             throw new DocumentException(e);
304         } finally {
305             ctx.closeConnection();
306         }
307     }
308
309     /**
310      * delete of a relationship deletes one or more relationships between
311      * permission and role
312      * the object and subjects of the relationship is chosen (by doc handler) from
313      * the payload
314      * @param ctx
315      * @param handler
316      * @return
317      * @throws DocumentNotFoundException
318      * @throws DocumentException
319      */
320     @Override
321     public boolean delete(ServiceContext ctx, String id, DocumentHandler handler)
322             throws DocumentNotFoundException, DocumentException {
323         boolean result = true;
324         
325         JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();      
326         try {
327                 jpaTransactionContext.beginTransaction();
328             handler.prepare(Action.DELETE);
329             List<T> relationshipList = new ArrayList<T>();
330             DocumentWrapper<List<T>> wrapDoc = new DocumentWrapperImpl<List<T>>(relationshipList);
331             handler.handle(Action.DELETE, wrapDoc);
332             //
333             //the following could be much more efficient if done with a single sql/jql
334             //
335             for (T relationship : relationshipList) {
336                 jpaTransactionContext.remove(getRelationship(jpaTransactionContext, relationship));
337             }
338             handler.complete(Action.DELETE, wrapDoc); // Delete from the Spring Security tables.  Would be better if this was part of the earlier transaction.
339             jpaTransactionContext.commitTransaction();
340         } catch (DocumentException de) {
341                 jpaTransactionContext.markForRollback();
342             throw de;
343         } catch (Exception e) {
344                 jpaTransactionContext.markForRollback();
345             if (logger.isDebugEnabled()) {
346                 logger.debug("delete(ctx, ix, handler): Caught exception ", e);
347             }
348             throw new DocumentException(e);
349         } finally {
350                 ctx.closeConnection();
351         }
352         
353         return result;
354     }
355
356     /**
357      * getObjectId returns the id of the object in a relationship
358      * @param ctx
359      * @return
360      */
361     protected String getObjectId(ServiceContext ctx) {
362         String objectId = (String) ctx.getProperty(ServiceContextProperties.OBJECT_ID);
363         
364         if (objectId == null) {
365             String msg = ServiceContextProperties.OBJECT_ID + " property is missing in the context";
366             logger.error(msg);
367             throw new IllegalArgumentException(msg);
368         }
369
370         return objectId;
371     }
372
373     /**
374      * getObjectClass returns the class of the object in a relationship
375      * @param ctx
376      * @return
377      */
378     protected Class getObjectClass(ServiceContext ctx) {
379         Class objectClass = (Class) ctx.getProperty(ServiceContextProperties.OBJECT_CLASS);
380         
381         if (objectClass == null) {
382             String msg = ServiceContextProperties.OBJECT_CLASS + " property is missing in the context";
383             logger.error(msg);
384             throw new IllegalArgumentException(msg);
385         }
386         
387         return objectClass;
388     }
389
390     /**
391      * getObject returns the object in the relationship
392      * @param ctx
393      * @param id
394      * @return
395      */
396     protected Object getObject(ServiceContext ctx, String id)
397                 throws DocumentNotFoundException {
398         Class objectClass = getObjectClass(ctx);
399         return JpaStorageUtils.getEntity((JPATransactionContext)ctx.getCurrentTransactionContext(), id, objectClass);
400     }
401 }