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