]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
5ade3641c5a39a2846772ca4f8635c6eeb6f23c6
[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 2009 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 package org.collectionspace.services.common.storage.jpa;
19
20 import java.util.Date;
21 import java.util.List;
22
23 import javax.persistence.RollbackException;
24 import javax.xml.datatype.XMLGregorianCalendar;
25
26 import java.sql.BatchUpdateException;
27
28 import javax.persistence.EntityExistsException;
29 import javax.persistence.EntityManager;
30 import javax.persistence.EntityManagerFactory;
31 import javax.persistence.Query;
32
33 import org.collectionspace.services.common.document.BadRequestException;
34 import org.collectionspace.services.common.document.DocumentException;
35 import org.collectionspace.services.common.document.DocumentFilter;
36 import org.collectionspace.services.common.document.DocumentHandler;
37 import org.collectionspace.services.common.document.DocumentNotFoundException;
38 import org.collectionspace.services.common.document.DocumentHandler.Action;
39 import org.collectionspace.services.common.document.DocumentWrapper;
40 import org.collectionspace.services.common.document.DocumentWrapperImpl;
41 import org.collectionspace.services.common.document.JaxbUtils;
42 import org.collectionspace.services.common.document.TransactionException;
43 import org.collectionspace.services.common.storage.StorageClient;
44 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
45 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
46 import org.collectionspace.services.common.context.ServiceContextProperties;
47 import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils;
48 import org.collectionspace.services.common.authorization_mgt.AuthorizationStore;
49 import org.collectionspace.services.common.context.ServiceContext;
50 import org.collectionspace.services.common.query.QueryContext;
51 import org.collectionspace.services.lifecycle.TransitionDef;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * JpaStorageClient is used to perform CRUD operations on SQL storage using JPA.
57  * It uses @see DocumentHandler as IOHandler with the client.
58  * All the operations in this client are carried out under their own transactions.
59  * A call to any method would start and commit/rollback a transaction.
60  * 
61  * Assumption: each persistent entityReceived has the following 3 attributes
62 <xs:element name="createdAt" type="xs:dateTime">
63 <xs:annotation>
64 <xs:appinfo>
65 <hj:basic>
66 <orm:column name="created_at" nullable="false"/>
67 </hj:basic>
68 </xs:appinfo>
69 </xs:annotation>
70 </xs:element>
71 <xs:element name="updatedAt" type="xs:dateTime">
72 <xs:annotation>
73 <xs:appinfo>
74 <hj:basic>
75 <orm:column name="updated_at" />
76 </hj:basic>
77 </xs:appinfo>
78 </xs:annotation>
79 </xs:element>
80 </xs:sequence>
81 <xs:attribute name="csid" type="xs:string">
82 <xs:annotation>
83 <xs:appinfo>
84 <hj:csidReceived>
85 <orm:column name="csid" length="128" nullable="false"/>
86 </hj:csidReceived>
87 </xs:appinfo>
88 </xs:annotation>
89 </xs:attribute>
90  *
91  * $LastChangedRevision: $ $LastChangedDate: $
92  */
93 public class JpaStorageClientImpl implements StorageClient {
94
95     /** The logger. */
96     private final Logger logger = LoggerFactory.getLogger(JpaStorageClientImpl.class);
97
98     /**
99      * Instantiates a new jpa storage client.
100      */
101     public JpaStorageClientImpl() {
102         //intentionally empty
103     }
104     
105     /* (non-Javadoc)
106      * @see org.collectionspace.services.common.storage.StorageClient#create(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
107      */
108     @Override
109     public String create(ServiceContext ctx,
110             DocumentHandler handler) throws BadRequestException,
111             DocumentException {
112         if (ctx == null) {
113             throw new IllegalArgumentException(
114                     "create: ctx is missing");
115         }
116         if (handler == null) {
117             throw new IllegalArgumentException(
118                     "create: handler is missing");
119         }
120         
121         boolean rollbackTransaction = false;
122         EntityManagerFactory emf = null;
123         EntityManager em = null;
124         try {
125             handler.prepare(Action.CREATE);
126             Object entity = handler.getCommonPart();
127             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entity);
128             
129             emf = JpaStorageUtils.getEntityManagerFactory();            
130             em = emf.createEntityManager();
131             em.getTransaction().begin(); { //begin of transaction block            
132                     ctx.setProperty(AuthorizationStore.ENTITY_MANAGER_PROP_KEY, em);
133                     try {
134                         handler.handle(Action.CREATE, wrapDoc);
135                             JaxbUtils.setValue(entity, "setCreatedAtItem", Date.class, new Date());
136                             em.persist(entity);                 
137                     } catch (EntityExistsException ee) {
138                         //
139                         // We found an existing matching entity in the store, so we don't need to create one.  Just update the transient 'entity' instance with the existing persisted entity we found.
140                         // An entity's document handler class will throw this exception only if attempting to create (but not actually creating) duplicate is ok -e.g., Permission records.
141                         //
142                         entity = wrapDoc.getWrappedObject(); // the handler should have reset the wrapped transient object with the existing persisted entity we just found.
143                     }
144             }
145             em.getTransaction().commit();
146             handler.complete(Action.CREATE, wrapDoc);
147             return (String) JaxbUtils.getValue(entity, "getCsid");
148         } catch (BadRequestException bre) {
149                 rollbackTransaction = true;
150             throw bre;
151         } catch (DocumentException de) {
152                 rollbackTransaction = true;
153             throw de;
154         } catch (Exception e) {
155                 rollbackTransaction = true;
156             if (logger.isDebugEnabled()) {
157                 logger.debug("Caught exception ", e);
158             }
159             throw DocumentException.createDocumentException(e);
160         } finally {
161             ctx.setProperty(AuthorizationStore.ENTITY_MANAGER_PROP_KEY, null);
162             if (em != null) {
163                 if (rollbackTransaction == true) {
164                         if (em.getTransaction().isActive() == true) {
165                                 em.getTransaction().rollback();
166                         }
167                 }
168                 // Don't call this unless "em" is not null -hence the check above.
169                 JpaStorageUtils.releaseEntityManagerFactory(emf);
170             }
171         }
172
173     }
174
175     /* (non-Javadoc)
176      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
177      */
178     @Override
179     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
180             throws DocumentNotFoundException, DocumentException {
181         throw new UnsupportedOperationException();
182     }
183
184     /* (non-Javadoc)
185      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
186      */
187     @Override
188     public void get(ServiceContext ctx, String id, DocumentHandler handler)
189             throws DocumentNotFoundException, DocumentException {
190         if (ctx == null) {
191             throw new IllegalArgumentException(
192                     "get: ctx is missing");
193         }
194         if (handler == null) {
195             throw new IllegalArgumentException(
196                     "get: handler is missing");
197         }
198         EntityManagerFactory emf = null;
199         EntityManager em = null;
200         try {
201             handler.prepare(Action.GET);
202             Object o = null;
203             o = JpaStorageUtils.getEntity(getEntityName(ctx), id, 
204                     ctx.getTenantId());
205             if (null == o) {
206                 if (em != null && em.getTransaction().isActive()) {
207                     em.getTransaction().rollback();
208                 }
209                 String msg = "could not find entity with id=" + id;
210                 throw new DocumentNotFoundException(msg);
211             }
212             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(o);
213             handler.handle(Action.GET, wrapDoc);
214             handler.complete(Action.GET, wrapDoc);
215         } catch (DocumentException de) {
216             throw de;
217         } catch (Exception e) {
218             if (logger.isDebugEnabled()) {
219                 logger.debug("Caught exception ", e);
220             }
221             throw new DocumentException(e);
222         } finally {
223             if (emf != null) {
224                 JpaStorageUtils.releaseEntityManagerFactory(emf);
225             }
226         }
227     }
228
229     /* (non-Javadoc)
230      * @see org.collectionspace.services.common.storage.StorageClient#getAll(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
231      */
232     @Override
233     public void getAll(ServiceContext ctx, DocumentHandler handler)
234             throws DocumentNotFoundException, DocumentException {
235         throw new UnsupportedOperationException("use getFiltered instead");
236     }
237
238     /* (non-Javadoc)
239      * @see org.collectionspace.services.common.storage.StorageClient#getFiltered(org.collectionspace.services.common.context.ServiceContext, org.collectionspace.services.common.document.DocumentHandler)
240      */
241     @Override
242     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
243             throws DocumentNotFoundException, DocumentException {
244         QueryContext queryContext = new QueryContext(ctx, handler);
245         
246         DocumentFilter docFilter = handler.getDocumentFilter();
247         if (docFilter == null) {
248             docFilter = handler.createDocumentFilter();
249         }
250         EntityManagerFactory emf = null;
251         EntityManager em = null;
252         try {
253             handler.prepare(Action.GET_ALL);
254             StringBuilder queryStrBldr = new StringBuilder("SELECT a FROM ");
255             queryStrBldr.append(getEntityName(ctx));
256             queryStrBldr.append(" a");
257             
258             List<DocumentFilter.ParamBinding> params = docFilter.buildWhereForSearch(queryStrBldr);
259             emf = JpaStorageUtils.getEntityManagerFactory();
260             em = emf.createEntityManager();
261             String queryStr = queryStrBldr.toString(); //for debugging
262             Query q = em.createQuery(queryStr);
263             //bind parameters
264             for (DocumentFilter.ParamBinding p : params) {
265                 q.setParameter(p.getName(), p.getValue());
266             }
267             if (docFilter.getOffset() > 0) {
268                 q.setFirstResult(docFilter.getOffset());
269             }
270             if (docFilter.getPageSize() > 0) {
271                 q.setMaxResults(docFilter.getPageSize());
272             }
273
274             //FIXME is transaction required for get?
275             em.getTransaction().begin();
276             List list = q.getResultList();
277             long totalItems = getTotalItems(em, ctx, handler); // Find out how many items our query would find independent of the paging restrictions
278             em.getTransaction().commit();
279             
280             docFilter.setTotalItemsResult(totalItems); // Save the items total in the doc filter for later reporting
281
282             DocumentWrapper<List> wrapDoc = new DocumentWrapperImpl<List>(list);
283             handler.handle(Action.GET_ALL, wrapDoc);
284             handler.complete(Action.GET_ALL, wrapDoc);
285         } catch (DocumentException de) {
286             throw de;
287         } catch (Exception e) {
288             if (logger.isDebugEnabled()) {
289                 logger.debug("Caught exception ", e);
290             }
291             throw new DocumentException(e);
292         } finally {
293             if (emf != null) {
294                 JpaStorageUtils.releaseEntityManagerFactory(emf);
295             }
296         }
297     }
298
299     /*
300      * Return the COUNT for a query to find the total number of matches independent of the paging restrictions.
301      */
302     private long getTotalItems(EntityManager em, ServiceContext ctx, DocumentHandler handler) {
303         long result = -1;
304         
305         DocumentFilter docFilter = handler.getDocumentFilter();
306         StringBuilder queryStrBldr = new StringBuilder("SELECT COUNT(*) FROM ");
307         queryStrBldr.append(getEntityName(ctx));
308         queryStrBldr.append(" a");
309         
310         List<DocumentFilter.ParamBinding> params = docFilter.buildWhereForSearch(queryStrBldr);
311         String queryStr = queryStrBldr.toString();
312         Query q = em.createQuery(queryStr);
313         //bind parameters
314         for (DocumentFilter.ParamBinding p : params) {
315             q.setParameter(p.getName(), p.getValue());
316         }
317
318         result = (long) q.getSingleResult();
319
320         return result;
321     }
322
323         /* (non-Javadoc)
324      * @see org.collectionspace.services.common.storage.StorageClient#update(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
325      */
326     @Override
327     public void update(ServiceContext ctx, String id, DocumentHandler handler)
328             throws BadRequestException, DocumentNotFoundException,
329             DocumentException {
330         if (ctx == null) {
331             throw new IllegalArgumentException(
332                     "update: ctx is missing");
333         }
334         if (handler == null) {
335             throw new IllegalArgumentException(
336                     "update: handler is missing");
337         }
338         EntityManagerFactory emf = null;
339         EntityManager em = null;
340         try {
341             handler.prepare(Action.UPDATE);
342             Object entityReceived = handler.getCommonPart();
343             emf = JpaStorageUtils.getEntityManagerFactory();
344             em = emf.createEntityManager();
345             em.getTransaction().begin();
346             Object entityFound = getEntity(em, id, entityReceived.getClass());
347             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
348             handler.handle(Action.UPDATE, wrapDoc);
349             JaxbUtils.setValue(entityFound, "setUpdatedAtItem", Date.class, new Date());
350             em.getTransaction().commit();
351             handler.complete(Action.UPDATE, wrapDoc);
352         } catch (BadRequestException bre) {
353             if (em != null && em.getTransaction().isActive()) {
354                 em.getTransaction().rollback();
355             }
356             throw bre;
357         } catch (DocumentException de) {
358             if (em != null && em.getTransaction().isActive()) {
359                 em.getTransaction().rollback();
360             }
361             throw de;
362         } catch (Exception e) {
363             if (logger.isDebugEnabled()) {
364                 logger.debug("Caught exception ", e);
365             }
366             throw new DocumentException(e);
367         } finally {
368             if (emf != null) {
369                 JpaStorageUtils.releaseEntityManagerFactory(emf);
370             }
371         }
372     }
373
374     /* 
375      * delete removes entity and its child entities
376      * cost: a get before delete
377      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
378      */
379     @Override
380     public void delete(ServiceContext ctx, String id)
381             throws DocumentNotFoundException,
382             DocumentException {
383
384         if (logger.isDebugEnabled()) {
385             logger.debug("delete(ctx, id): deleting entity with id=" + id);
386         }
387
388         if (ctx == null) {
389             throw new IllegalArgumentException(
390                     "delete(ctx, id): ctx is missing");
391         }
392         EntityManagerFactory emf = null;
393         EntityManager em = null;
394         try {
395
396             emf = JpaStorageUtils.getEntityManagerFactory();
397             em = emf.createEntityManager();
398
399             em.getTransaction().begin();
400             Object entityFound = getEntity(ctx, em, id);
401             if (entityFound == null) {
402                 if (em != null && em.getTransaction().isActive()) {
403                     em.getTransaction().rollback();
404                 }
405                 String msg = "delete(ctx, id): could not find entity with id=" + id;
406                 logger.error(msg);
407                 throw new DocumentNotFoundException(msg);
408             }
409             em.remove(entityFound);
410             em.getTransaction().commit();
411
412         } catch (DocumentException de) {
413             if (em != null && em.getTransaction().isActive()) {
414                 em.getTransaction().rollback();
415             }
416             throw de;
417         } catch (Exception e) {
418             if (logger.isDebugEnabled()) {
419                 logger.debug("delete(ctx, id): Caught exception ", e);
420             }
421             if (em != null && em.getTransaction().isActive()) {
422                 em.getTransaction().rollback();
423             }
424             throw new DocumentException(e);
425         } finally {
426             if (emf != null) {
427                 JpaStorageUtils.releaseEntityManagerFactory(emf);
428             }
429         }
430     }
431
432     /**
433      * deleteWhere uses the where clause to delete an entityReceived represented by the csidReceived
434      * it does not delete any child entities.
435      * @param ctx
436      * @param id
437      * @throws DocumentNotFoundException
438      * @throws DocumentException
439      */
440     public void deleteWhere(ServiceContext ctx, String id)
441             throws DocumentNotFoundException,
442             DocumentException {
443
444         if (ctx == null) {
445             throw new IllegalArgumentException(
446                     "deleteWhere(ctx, id) : ctx is missing");
447         }
448
449         if (logger.isDebugEnabled()) {
450             logger.debug("deleteWhere(ctx, id): deleting entity with id=" + id);
451         }
452         EntityManagerFactory emf = null;
453         EntityManager em = null;
454         try {
455             StringBuilder deleteStr = new StringBuilder("DELETE FROM ");
456             deleteStr.append(getEntityName(ctx));
457             deleteStr.append(" WHERE csid = :csid and tenantId = :tenantId");
458             //TODO: add tenant csidReceived
459
460             emf = JpaStorageUtils.getEntityManagerFactory();
461             em = emf.createEntityManager();
462             Query q = em.createQuery(deleteStr.toString());
463             q.setParameter("csid", id);
464             q.setParameter("tenantId", ctx.getTenantId());
465
466             int rcount = 0;
467             em.getTransaction().begin();
468             rcount = q.executeUpdate();
469             if (rcount != 1) {
470                 if (em != null && em.getTransaction().isActive()) {
471                     em.getTransaction().rollback();
472                 }
473                 String msg = "deleteWhere(ctx, id) could not find entity with id=" + id;
474                 logger.error(msg);
475                 throw new DocumentNotFoundException(msg);
476             }
477             em.getTransaction().commit();
478
479         } catch (DocumentException de) {
480             if (em != null && em.getTransaction().isActive()) {
481                 em.getTransaction().rollback();
482             }
483             throw de;
484         } catch (Exception e) {
485             if (logger.isDebugEnabled()) {
486                 logger.debug("deleteWhere(ctx, id) Caught exception ", e);
487             }
488             if (em != null && em.getTransaction().isActive()) {
489                 em.getTransaction().rollback();
490             }
491             throw new DocumentException(e);
492         } finally {
493             if (emf != null) {
494                 JpaStorageUtils.releaseEntityManagerFactory(emf);
495             }
496         }
497     }
498
499     /*
500      * delete removes entity and its child entities but calls back to given handler
501      * cost: a get before delete
502      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String)
503      */
504     @Override
505     public boolean delete(ServiceContext ctx, String id, DocumentHandler handler)
506             throws DocumentNotFoundException, DocumentException {
507         boolean result = true;
508         
509         if (ctx == null) {
510             throw new IllegalArgumentException(
511                     "delete(ctx, ix, handler): ctx is missing");
512         }
513         if (handler == null) {
514             throw new IllegalArgumentException(
515                     "delete(ctx, ix, handler): handler is missing");
516         }
517         
518         EntityManagerFactory emf = null;
519         EntityManager em = null;
520         try {
521             handler.prepare(Action.DELETE);
522
523             emf = JpaStorageUtils.getEntityManagerFactory();
524             em = emf.createEntityManager();
525
526             em.getTransaction().begin();
527             Object entityFound = getEntity(ctx, em, id);
528             if (entityFound == null) {
529                 if (em != null && em.getTransaction().isActive()) {
530                     em.getTransaction().rollback();
531                 }
532                 String msg = "delete(ctx, ix, handler) could not find entity with id=" + id;
533                 logger.error(msg);
534                 throw new DocumentNotFoundException(msg);
535             }
536             DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(entityFound);
537             handler.handle(Action.DELETE, wrapDoc);
538             em.remove(entityFound);
539             em.getTransaction().commit();
540             handler.complete(Action.DELETE, wrapDoc);
541         } catch (DocumentException de) {
542             if (em != null && em.getTransaction().isActive()) {
543                 em.getTransaction().rollback();
544             }
545             throw de;
546         } catch (Exception e) {
547             if (logger.isDebugEnabled()) {
548                 logger.debug("delete(ctx, ix, handler): Caught exception ", e);
549             }
550             if (em != null && em.getTransaction().isActive()) {
551                 em.getTransaction().rollback();
552             }
553             throw new DocumentException(e);
554         } finally {
555             if (emf != null) {
556                 JpaStorageUtils.releaseEntityManagerFactory(emf);
557             }
558         }
559         
560         return result;
561     }
562
563     /**
564      * Gets the entityReceived name.
565      * 
566      * @param ctx the ctx
567      * 
568      * @return the entityReceived name
569      */
570     protected String getEntityName(ServiceContext ctx) {
571         Object o = ctx.getProperty(ServiceContextProperties.ENTITY_NAME);
572         if (o == null) {
573             throw new IllegalArgumentException(ServiceContextProperties.ENTITY_NAME
574                     + "property is missing in context "
575                     + ctx.toString());
576         }
577
578         return (String) o;
579     }
580
581     /**
582      * getEntity returns persistent entity for given id. it assumes that
583      * service context has property ServiceContextProperties.ENTITY_CLASS set
584      * rolls back the transaction if not found
585      * @param ctx service context
586      * @param em entity manager
587      * @param csid received
588      * @return
589      * @throws DocumentNotFoundException and rollsback the transaction if active
590      */
591     protected Object getEntity(ServiceContext ctx, EntityManager em, String id)
592             throws DocumentNotFoundException {
593         Class entityClazz = (Class) ctx.getProperty(ServiceContextProperties.ENTITY_CLASS);
594         if (entityClazz == null) {
595             String msg = ServiceContextProperties.ENTITY_CLASS
596                     + " property is missing in the context";
597             logger.error(msg);
598             throw new IllegalArgumentException(msg);
599         }
600         return getEntity(em, id, entityClazz);
601     }
602
603     /**
604      * getEntity retrieves the persistent entity of given class for given id
605      * rolls back the transaction if not found
606      * @param em
607      * @param id entity id
608      * @param entityClazz
609      * @return
610      * @throws DocumentNotFoundException and rollsback the transaction if active
611      */
612     protected Object getEntity(EntityManager em, String id, Class entityClazz)
613             throws DocumentNotFoundException {
614         Object entityFound = JpaStorageUtils.getEntity(em, id, entityClazz);
615         if (entityFound == null) {
616             if (em != null && em.getTransaction().isActive()) {
617                 em.getTransaction().rollback();
618             }
619             String msg = "could not find entity of type=" + entityClazz.getName()
620                     + " with id=" + id;
621             logger.error(msg);
622             throw new DocumentNotFoundException(msg);
623         }
624         return entityFound;
625     }
626
627     @Override
628     public void get(ServiceContext ctx, DocumentHandler handler)
629             throws DocumentNotFoundException, DocumentException {
630         throw new UnsupportedOperationException();
631     }
632
633         @Override
634         public void doWorkflowTransition(ServiceContext ctx, String id,
635                         DocumentHandler handler, TransitionDef transitionDef)
636                         throws BadRequestException, DocumentNotFoundException,
637                         DocumentException {
638                 // Do nothing.  JPA services do not support workflow.
639         }
640
641         @Override
642         public void deleteWithWhereClause(ServiceContext ctx, String whereClause,
643                         DocumentHandler handler) throws DocumentNotFoundException,
644                         DocumentException {
645         throw new UnsupportedOperationException();
646         }
647
648         @Override
649         public boolean synchronize(ServiceContext ctx, Object specifier,
650                         DocumentHandler handler) throws DocumentNotFoundException,
651                         TransactionException, DocumentException {
652                 // TODO Auto-generated method stub
653                 // Do nothing. Subclasses can override if they want/need to.
654                 return true;
655         }
656         
657         @Override
658         public boolean synchronizeItem(ServiceContext ctx, AuthorityItemSpecifier itemSpecifier,
659                         DocumentHandler handler) throws DocumentNotFoundException,
660                         TransactionException, DocumentException {
661                 // TODO Auto-generated method stub
662                 // Do nothing. Subclasses can override if they want/need to.
663                 return true;
664         }
665
666 }