]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-6937-A: Dealing with sync-related soft and hard deletes.
authorremillet <remillet@yahoo.com>
Wed, 20 Apr 2016 02:40:14 +0000 (19:40 -0700)
committerremillet <remillet@yahoo.com>
Wed, 20 Apr 2016 02:40:14 +0000 (19:40 -0700)
21 files changed:
1  2 
services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java
services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java
services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java
services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityServiceUtils.java
services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java
services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java
services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/CreateAndLinkLoanOutBatchJob.java
services/batch/service/src/main/java/org/collectionspace/services/batch/nuxeo/UpdateObjectLocationBatchJob.java
services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java
services/client/src/main/java/org/collectionspace/services/client/PoxPayloadOut.java
services/common/src/main/java/org/collectionspace/services/common/AbstractCollectionSpaceResourceImpl.java
services/common/src/main/java/org/collectionspace/services/common/CollectionSpaceResource.java
services/common/src/main/java/org/collectionspace/services/common/NuxeoBasedResource.java
services/common/src/main/java/org/collectionspace/services/common/ResourceMap.java
services/common/src/main/java/org/collectionspace/services/common/ResourceMapImpl.java
services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java
services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java
services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java
services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java
services/common/src/main/java/org/collectionspace/services/common/document/DocumentReferenceException.java
services/common/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java

index 81fa6b8e98426405e55d8a8b574a41bb3aea9fd0,81fa6b8e98426405e55d8a8b574a41bb3aea9fd0..d1a5cd6d29955fa2e613bdad575fddbeaea06fe0
@@@ -25,6 -25,6 +25,8 @@@ package org.collectionspace.services.ja
  import org.collectionspace.services.account.AccountResource;
  import org.collectionspace.services.account.TenantResource;
  import org.collectionspace.services.blob.BlobResource;
++import org.collectionspace.services.client.PoxPayloadIn;
++import org.collectionspace.services.client.PoxPayloadOut;
  import org.collectionspace.services.collectionobject.CollectionObjectResource;
  import org.collectionspace.services.id.IDResource;
  import org.collectionspace.services.media.MediaResource;
@@@ -62,6 -62,6 +64,8 @@@ import javax.ws.rs.core.Application
  import java.util.HashSet;
  import java.util.Set;
  
++
++
  //import org.collectionspace.services.common.FileUtils;
  import org.collectionspace.services.authorization.PermissionResource;
  import org.collectionspace.services.authorization.RoleResource;
@@@ -86,7 -86,7 +90,7 @@@ public class CollectionSpaceJaxRsApplic
  
      private Set<Object> singletons = new HashSet<Object>();
      private Set<Class<?>> empty = new HashSet<Class<?>>();    
--    private ResourceMap resourceMap = new ResourceMapImpl();
++    private ResourceMap<PoxPayloadIn, PoxPayloadOut> resourceMap = new ResourceMapImpl();
      private ServletContext servletContext = null;
  
      public CollectionSpaceJaxRsApplication() {        
index 8bca8c47f7b8ec907e7bcb67cae25ee861250bbc,8bca8c47f7b8ec907e7bcb67cae25ee861250bbc..bb2e159e2c29effaaa90235ed961da5b65dbda1b
@@@ -278,5 -278,5 +278,4 @@@ public class AccountResource extends Se
              throw bigReThrow(e, ServiceMessages.DELETE_FAILED, accCsid);
          }
      }
--    
  }
index b5b0de9acad21a211300de32a4fa40a5fd754163,c52f3cf3fde517bec65f4612f20a0b81f9909996..916d9660b51ab77a39d30586db6e3f3acbe80bf2
@@@ -126,7 -120,37 +116,6 @@@ public abstract class AuthorityResource
        
      final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
  
 -    protected Specifier getSpecifier(String specifierIn, String method, String op) throws CSWebApplicationException {
 -        if (logger.isDebugEnabled()) {
 -            logger.debug("getSpecifier called by: " + method + " with specifier: " + specifierIn);
 -        }
 -        if (specifierIn != null) {
 -            if (!specifierIn.startsWith(URN_PREFIX)) {
 -                // We'll assume it is a CSID and complain if it does not match
 -                return new Specifier(SpecifierForm.CSID, specifierIn);
 -            } else {
 -                if (specifierIn.startsWith(URN_PREFIX_NAME, URN_PREFIX_LEN)) {
 -                    int closeParen = specifierIn.indexOf(')', URN_NAME_PREFIX_LEN);
 -                    if (closeParen >= 0) {
 -                        return new Specifier(SpecifierForm.URN_NAME,
 -                                specifierIn.substring(URN_NAME_PREFIX_LEN, closeParen));
 -                    }
 -                } else if (specifierIn.startsWith(URN_PREFIX_ID, URN_PREFIX_LEN)) {
 -                    int closeParen = specifierIn.indexOf(')', URN_ID_PREFIX_LEN);
 -                    if (closeParen >= 0) {
 -                        return new Specifier(SpecifierForm.CSID,
 -                                specifierIn.substring(URN_ID_PREFIX_LEN, closeParen));
 -                    }
 -                }
 -            }
 -        }
 -        logger.error(method + ": bad or missing specifier!");
 -        Response response = Response.status(Response.Status.BAD_REQUEST).entity(
 -                op + " failed on bad or missing Authority specifier").type(
 -                "text/plain").build();
 -        throw new CSWebApplicationException(response);
 -    }
--
      /**
       * Instantiates a new Authority resource.
       */
          try {
              ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
              AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
 -            specifier = getSpecifier(csid, "getAuthority", "GET");
 +            specifier = Specifier.getSpecifier(csid, "getAuthority", "GET");
+             handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
              neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
              payloadOut = ctx.getOutput();
          } catch (Exception e) {
              throws Exception {
          AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
          Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
--        if (shouldUpdateRev != null && shouldUpdateRev == true) {
--              handler.setShouldUpdateRevNumber(true);
++        if (shouldUpdateRev != null) {
++              handler.setShouldUpdateRevNumber(shouldUpdateRev);
          }
          getRepositoryClient(ctx).update(ctx, csid, handler);
          return ctx.getOutput();
          }
      }
      
-     protected Response createAuthorityItem(ServiceContext ctx, String parentIdentifier, boolean shouldUpdateRevNumber) throws Exception {
+     /**
+      * 
+      * @param ctx
+      * @param parentspecifier         - ID of the container. Can be URN or CSID form
+      * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
+      * @param proposed                                - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
+      * @return
+      * @throws Exception
+      */
 -    protected Response createAuthorityItem(ServiceContext ctx, String parentspecifier,
++    protected Response createAuthorityItem(ServiceContext ctx, String parentIdentifier,
+               boolean shouldUpdateRevNumber,
+               boolean proposed) throws Exception {
        Response result = null;
        
          // Note: must have the parentShortId, to do the create.
          AuthorityItemDocumentModelHandler handler = 
                (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
          handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
+         handler.setIsProposed(proposed);
 +        // Make the client call
          String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
 +        
 +        // Build the JAX-RS response
          UriBuilder path = UriBuilder.fromResource(resourceClass);
          path.path(parent.CSID + "/items/" + itemcsid);
          result = Response.created(path.build()).build();
       * @throws Exception
       */
      public Response createAuthorityItemWithParentContext(ServiceContext parentCtx,
 -              String parentspecifier,
 +              String parentIdentifier,
                PoxPayloadIn input,
-               boolean shouldUpdateRevNumber) throws Exception {
+               boolean shouldUpdateRevNumber,
+               boolean isProposed) throws Exception {
        Response result = null;
        
          ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
          if (parentCtx.getCurrentRepositorySession() != null) {
                ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
          }
-         result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber);
 -        result = this.createAuthorityItem(ctx, parentspecifier, shouldUpdateRevNumber, isProposed);
++        result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed);
  
        return result;
      }
          try {
              PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
              ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
-             result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV);
 -            result = this.createAuthorityItem(ctx, parentspecifier, AuthorityServiceUtils.UPDATE_REV,
++            result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
+                       AuthorityServiceUtils.PROPOSED);
          } catch (Exception e) {
              throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
          }
              handler.setDocumentFilter(myFilter);
              getRepositoryClient(ctx).get(ctx, handler);
          }
 -        // TODO should we assert that the item is in the passed vocab?
 +        
          result = (PoxPayloadOut) ctx.getOutput();
-               String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), AuthorityItemJAXBSchema.REF_NAME);
 +        if (result != null) {
++              String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
 +              if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
 +                      throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
 +                                      itemSpec.value, inAuthority, parentcsid));
 +              }
 +        }
 +        
  
        return result;
      }
        }
      
      /**
 -     * Return a paged list of authority items.
 +     * Gets the authorityItem list for the specified authority
 +     * If partialPerm is specified, keywords will be ignored.
       * 
 -     * @param existingContext
 -     * @param specifier
 -     * @param uriInfo
 -     * @return
 -     * @throws Exception
 +     * @param specifier either a CSID or one of the urn forms
 +     * @param partialTerm if non-null, matches partial terms
 +     * @param keywords if non-null, matches terms in the keyword index for items
 +     * @param ui passed to include additional parameters, like pagination controls
-      * 
-      * @return the authorityItem list
++     *
       */
-     @GET
-     @Path("{csid}/items")
-     @Produces("application/xml")
-     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String specifier,
-             @Context UriInfo uriInfo) {
+     public AbstractCommonList getAuthorityItemList(ServiceContext existingContext,
+               String specifier,
+             UriInfo uriInfo) throws Exception {
        AbstractCommonList result = null;
 -
 -        ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
 -        MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
 +      
-         try {
 +            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
 +            MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
+         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
+               ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
+               ctx.setProperties(existingContext.getProperties());
+         }
 -                
 -        String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
 -        String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
 -        String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
 -        String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
 -        String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
 +            
 +            String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
 +            String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
 +            String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
 +            String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
 +            String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
  
 -        // For the wildcard case, parentcsid is null, but docHandler will deal with this.
 -        // We omit the parentShortId, only needed when doing a create...
 -        String parentcsid = PARENT_WILDCARD.equals(specifier) ? null :
 -                      lookupParentCSID(specifier, "getAuthorityItemList", "LIST", uriInfo);
 -        DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
 -              createItemDocumentHandler(ctx, parentcsid, null);
 -        
 -        DocumentFilter myFilter = handler.getDocumentFilter();
 -        // If we are not wildcarding the parent, add a restriction
 -        if (parentcsid != null) {
 -            myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
 -                    + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
 -                    + "'" + parentcsid + "'",
 -                    IQueryManager.SEARCH_QUALIFIER_AND);
 -        }
 +            // For the wildcard case, parentcsid is null, but docHandler will deal with this.
 +            // We omit the parentShortId, only needed when doing a create...
 +            String parentcsid = PARENT_WILDCARD.equals(specifier) ? null :
 +                              lookupParentCSID(specifier, "getAuthorityItemList", "LIST", uriInfo);
 +            DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
 +              createItemDocumentHandler(ctx, parentcsid, null);
 +            
 +            DocumentFilter myFilter = handler.getDocumentFilter();
 +            // If we are not wildcarding the parent, add a restriction
 +            if (parentcsid != null) {
 +                  myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
 +                          + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
 +                          + "'" + parentcsid + "'",
 +                          IQueryManager.SEARCH_QUALIFIER_AND);
 +            }
  
 -        if (Tools.notBlank(termStatus)) {
 -              // Start with the qualified termStatus field
 -              String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
 -                    + AuthorityItemJAXBSchema.TERM_STATUS;
 -              String[] filterTerms = termStatus.trim().split("\\|");
 -              String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
 -            myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
 -        }
 +            if (Tools.notBlank(termStatus)) {
 +              // Start with the qualified termStatus field
 +              String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
 +                        + AuthorityItemJAXBSchema.TERM_STATUS;
 +              String[] filterTerms = termStatus.trim().split("\\|");
 +              String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
 +                myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
 +            }
  
 -        result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);       
 +            result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);            
+       
+       return result;
+     }
+     
+     /**
+      * Gets the authorityItem list for the specified authority
+      * If partialPerm is specified, keywords will be ignored.
+      * 
+      * @param specifier either a CSID or one of the urn forms
+      * @param partialTerm if non-null, matches partial terms
+      * @param keywords if non-null, matches terms in the keyword index for items
+      * @param ui passed to include additional parameters, like pagination controls
+      * 
+      * @return the authorityItem list
+      */
+     @GET
+     @Path("{csid}/items")
+     @Produces("application/xml")
+     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String specifier,
+             @Context UriInfo uriInfo) {
+       AbstractCommonList result = null;
+       
+         try {
+             result = getAuthorityItemList(NULL_CONTEXT, specifier, uriInfo);    
          } catch (Exception e) {
              throw bigReThrow(e, ServiceMessages.LIST_FAILED);
          }
              @Context UriInfo uriInfo) {
          AuthorityRefDocList authRefDocList = null;
          try {
 -        ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
 -        MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
 -        //
+             authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
+         } catch (Exception e) {
+             throw bigReThrow(e, ServiceMessages.GET_FAILED);
+         }
+         
+         if (authRefDocList == null) {
+             Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                     "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
+                     "text/plain").build();
+             throw new CSWebApplicationException(response);
+         }
+         return authRefDocList;
+     }
+     
+     public AuthorityRefDocList getReferencingObjects(
+               ServiceContext existingContext,
+             String parentspecifier,
+             String itemspecifier,
+             UriTemplateRegistry uriTemplateRegistry,
+             UriInfo uriInfo) throws Exception {
+       AuthorityRefDocList authRefDocList = null;
+       
 -        String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
 -        String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
 +            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
 +            MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
 +
+         // Merge parts of existing context with our new context
+         //
+         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
+               ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());  // If one exists, use the existing repo session
+               ctx.setProperties(existingContext.getProperties());
+         }
 +            String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
 +            String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
  
 -        List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
 -        if(serviceTypes == null || serviceTypes.isEmpty()) {
 -              serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
 -        }
 -        
 -        // Note that we have to create the service handler for the Items, not the main parent resource
 -        // We omit the parentShortId, only needed when doing a create...
 +            List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
 +            if(serviceTypes == null || serviceTypes.isEmpty()) {
 +              serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
 +            }
 +            
 +            // Note that we have to create the service context for the Items, not the main service
 +            // We omit the parentShortId, only needed when doing a create...
-             AuthorityItemDocumentModelHandler<?> handler = (AuthorityItemDocumentModelHandler<?>)
-                                                                                               createItemDocumentHandler(ctx, parentcsid, null);
+         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
+         authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
  
-             authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
-         } catch (Exception e) {
-             throw bigReThrow(e, ServiceMessages.GET_FAILED);
-         }
-         if (authRefDocList == null) {
-             Response response = Response.status(Response.Status.NOT_FOUND).entity(
-                     "Get failed, the requested Item CSID:" + itemspecifier + ": was not found.").type(
-                     "text/plain").build();
-             throw new CSWebApplicationException(response);
-         }
 -      return authRefDocList;
 +        return authRefDocList;
      }
  
      /**
              String itemIdentifier) throws Exception {
        PoxPayloadOut result = null;
          AuthorityItemSpecifier specifier;
--        CsidAndShortIdentifier parent;
        boolean neededSync = false;
  
--        parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
++      CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
          AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
 -        Specifier parentSpecifier = getSpecifier(parent.CSID, "getAuthority", "GET");
 -        Specifier itemSpecifier = getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
+         handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
+         // Create an authority item specifier
 +        Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
 +        Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
          specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
          //
          neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
       * Using the parent and item ID, sync the local item with the SAS (shared authority server)
       * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
       * local authority.  The parent context was created for the authority (parent) because the sync started there.
--     * @param parentCtx
++     * @param existingCtx
       * @param parentIdentifier
       * @param itemIdentifier
       * @return
       * @throws Exception
       */
--    public PoxPayloadOut synchronizeItemWithParentContext(
--              ServiceContext parentCtx,
++    public PoxPayloadOut synchronizeItemWithExistingContext(
++              ServiceContext existingCtx,
              String parentIdentifier,
              String itemIdentifier
              ) throws Exception {
        PoxPayloadOut result = null;
        
          ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
--                      parentCtx.getResourceMap(),
--                      parentCtx.getUriInfo());
--        if (parentCtx.getCurrentRepositorySession() != null) {
--              ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
++                      existingCtx.getResourceMap(),
++                      existingCtx.getUriInfo());
++        if (existingCtx.getCurrentRepositorySession() != null) {
++              ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
          }
          result = synchronizeItem(ctx, parentIdentifier, itemIdentifier);
        
      public Response deleteAuthorityItem(
              @PathParam("csid") String parentcsid,
              @PathParam("itemcsid") String itemcsid) {
 -      
+       Response result = null;
 +        //try{
          if (logger.isDebugEnabled()) {
              logger.debug("deleteAuthorityItem with parentcsid=" + parentcsid + " and itemcsid=" + itemcsid);
          }
+         
          try {
-             ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
-             ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
+             deleteAuthorityItem(null, parentcsid, itemcsid);
+             result = Response.status(HttpResponseCodes.SC_OK).build();
+         } catch (Exception e) {
+             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemcsid + " parentcsid:" + parentcsid);
+         }
 -        
 +            //Laramie, removing this catch, since it will surely fail below, since itemcsid or parentcsid will be null.
+         return result;
+     }
 -    
 +            // }catch (Throwable t){
+     public void deleteAuthorityItem(ServiceContext existingCtx,
+             String parentcsid,
+             String itemcsid) throws Exception {
+       Response result = null;
 -      
 +            //    System.out.println("ERROR in setting up DELETE: "+t);
+         ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
+         ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
 -        // Note that we have to create the service context for the Items, not the main service
 -        ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
 +            // }
-             // try {
-             // Note that we have to create the service context for the Items, not the main service
 +            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
+         if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
+               ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Use existing repo session if one exists
+               ctx.setProperties(existingCtx.getProperties());
+         }
 -        DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
 -        getRepositoryClient(ctx).delete(ctx, itemcsid, handler);
 -    }
 -    
 +            DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
 +            getRepositoryClient(ctx).delete(ctx, itemcsid, handler);
-             return Response.status(HttpResponseCodes.SC_OK).build();
-         } catch (Exception e) {
-             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemcsid + " parentcsid:" + parentcsid);
 +        }
-     }
-     public final static String hierarchy = "hierarchy";
 +
      @GET
      @Path("{csid}/items/{itemcsid}/" + hierarchy)
      @Produces("application/xml")
index 8f42ae34f4dddaf2267ddb740574e1596127141e,d192bf53b0ee54014c9855a30d2fcd49495fd8c6..14b346c19a765472f101c61dc76b7ab4fcdeed61
@@@ -14,21 -14,28 +14,28 @@@ import org.slf4j.LoggerFactory
  
  public class AuthorityServiceUtils {
      private static final Logger logger = LoggerFactory.getLogger(AuthorityIdentifierUtils.class);
 -
 +    //
-     // Revision property statics
-     //
+     // Used to keep track if an authority item's is deprecated
+     public static final String IS_DEPRECATED_PROPERTY = "IS_DEPRECATED_PROPERTY";
+     public static final Boolean DEPRECATED = true;
+     public static final Boolean NOT_DEPRECATED = !DEPRECATED;
+     
+     // Used to keep track if an authority item's rev number should be updated
      public static final String SHOULD_UPDATE_REV_PROPERTY = "SHOULD_UPDATE_REV_PROPERTY";
-     public static final boolean DONT_UPDATE_REV = false;
      public static final boolean UPDATE_REV = true;
 -    
+     public static final boolean DONT_UPDATE_REV = !UPDATE_REV;
++
+     // Used to keep track if an authority item is a locally proposed member of a SAS authority
+     public static final String IS_PROPOSED_PROPERTY = "IS_PROPOSED";
+     public static final Boolean PROPOSED = true;
+     public static final Boolean NOT_PROPOSED = !PROPOSED;
+     public static final Boolean NO_CHANGE = null;
  
-     //
-     // Makes a call to the SAS server for a authority payload
-     //
      static public PoxPayloadIn requestPayloadIn(ServiceContext ctx, Specifier specifier, Class responseType) throws Exception {
        PoxPayloadIn result = null;
        
          AuthorityClient client = (AuthorityClient) ctx.getClient();
--        Response res = client.read(specifier.value);
++        Response res = client.read(specifier.getURNValue());
          try {
                int statusCode = res.getStatus();
        
index bc6d2ee729426e2c39438186577a4380a53b17ca,72ccc315aa98c4db68b014a49fd6252bc559466f..a6a12ff359f368a9eec7cfb1a1b7a955914a83aa
@@@ -131,11 -136,12 +136,12 @@@ public abstract class AuthorityDocument
          //
          // Using the short ID of the local authority, create a URN specifier to retrieve the SAS authority
          //
-         Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, Specifier.createShortIdURNValue(shortId));
 -        Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, RefNameUtils.createShortIdRefName(shortId));
++        Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, shortId);
          PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(ctx, sasSpecifier, getEntityResponseType());
          //
 -        // Compare revision number of local authority with that of the shared authority on the SAS.
          // If the authority on the SAS is newer, synch all the items and then the authority record as well
          //
++        //
          Long sasRev = getRevision(sasPayloadIn);
          if (sasRev > localRev) {
                //
                //
                // Next, sync the authority resource/record itself
                //
 -              ResourceMap resourceMap = ctx.getResourceMap();
 -              String resourceName = ctx.getClient().getServiceName();
 -              AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
 -              // Set this context property since we don't want to update the revision number on sync updates
 -              ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, AuthorityServiceUtils.DONT_UPDATE_REV);
 -              PoxPayloadOut payloadOut = authorityResource.update(ctx, resourceMap, ctx.getUriInfo(), docModel.getName(), 
 +              AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
++              ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, AuthorityServiceUtils.DONT_UPDATE_REV); // Don't update the rev number, use the rev number for the SAS instance instead
 +              PoxPayloadOut payloadOut = authorityResource.update(ctx, ctx.getResourceMap(), ctx.getUriInfo(), docModel.getName(), 
                                sasPayloadIn);
                if (payloadOut != null) {
                        ctx.setOutput(payloadOut);
        //
        // Iterate over the list of items/terms in the remote authority
        //
-         PoxPayloadIn sasPayloadInItemList = getPayloadInItemList(ctx, sasAuthoritySpecifier);
 -        PoxPayloadIn sasPayloadInItemList = getPayloadInItemList(ctx, sasSpecifier);
++        PoxPayloadIn sasPayloadInItemList = requestPayloadInItemList(ctx, sasAuthoritySpecifier);
          List<Element> itemList = getItemList(sasPayloadInItemList);
          if (itemList != null) {
                for (Element e:itemList) {
                        totalItemsProcessed++;
                }
          }
 -        //
+         // Now see if we need to deprecate or delete items that have been hard-deleted from the SAS but still exist
+         // locally.  Subtract (remove) the list of remote items from the list of local items to determine which
+         // of the remote items have been hard deleted.
+         //
 -      ArrayList<String> itemsInLocalAuthority = getItemsInLocalAuthority(ctx, sasSpecifier);
++      ArrayList<String> itemsInLocalAuthority = getItemsInLocalAuthority(ctx, sasAuthoritySpecifier);
+       if (itemsInLocalAuthority.removeAll(itemsInRemoteAuthority) == true) {
+               ArrayList<String> remainingItems = itemsInLocalAuthority; // now a subset of local items
+               //
+               // We now need to either hard-deleted or deprecate the remaining authorities
+               //
+               deleteOrDeprecateItems(ctx, remainingItems);
+       }
          
          logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed));
          logger.info(String.format("Number of items synchronized: %d", synched));
  
          return result;
      }
 -    
++
++    /**
++     * 
++     * @param ctx
++     * @param refNameList
++     * @return
++     * @throws Exception
++     */
+     private long deleteOrDeprecateItems(ServiceContext ctx, ArrayList<String> refNameList) throws Exception {
+       long result = 0;
 -      ArrayList<String> failureList = new ArrayList<String>();
+       
++      ArrayList<String> failureList = new ArrayList<String>();
+       for (String refName:refNameList) {
+               AuthorityTermInfo itemInfo = RefNameUtils.parseAuthorityTermInfo(refName);
+               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
+               try {
+                       authorityResource.deleteAuthorityItem(ctx, itemInfo.inAuthority.csid, itemInfo.csid);
+                       result++;
+               } catch (DocumentException de) {
 -                      //
 -                      // If we can't delete the item, it might be because there are existing records referencing it. So
 -                      // we need to mark the item as deprecated
 -                      //
+                       logger.info(String.format("Hit document exception trying to delete or deprecate '%s' during sync",
+                                       refName), de);
+                       boolean marked = markAuthorityItemAsDeprecated(ctx, itemInfo);
+                       if (marked == true) {
+                               result++;
+                       }
+               } catch (Exception e) {
+                       logger.warn(String.format("Unable to delete authority item '%s'", refName), e);
+               }
+       }
 -      
++
+       if (logger.isWarnEnabled() == true) {
+               if (result != refNameList.size()) {
 -                      logger.warn(String.format("Unable to delete or deprecate some authority items during synchronization with SAS.  Deleted or deprecated %d of %d",
++                      logger.warn(String.format("Unable to delete or deprecate some authority items during synchronization with SAS.  Deleted or deprecated %d of %d.  See the services log file for details.",
+                                       result, refNameList.size()));
+               }
+       }
+       
+       return result;
+     }
+     
+     /**
+      * Mark the authority item as deprecated.
+      * 
+      * @param ctx
+      * @param itemInfo
+      * @throws Exception
+      */
+     private boolean markAuthorityItemAsDeprecated(ServiceContext ctx, AuthorityTermInfo itemInfo) throws Exception {
+       boolean result = false;
+       
+       try {
+               String itemCsid = itemInfo.csid;
+               DocumentModel docModel = NuxeoUtils.getDocFromCsid(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(), itemCsid);
+               docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.DEPRECATED,
+                               new Boolean(AuthorityServiceUtils.DEPRECATED));
++              CoreSessionInterface session = (CoreSessionInterface) ctx.getCurrentRepositorySession();
++              session.saveDocument(docModel);
+               result = true;
+       } catch (Exception e) {
+               logger.warn(String.format("Could not mark item '%s' as deprecated.", itemInfo.name), e);
+       }
+       
+       return result;
+     }
+     
+     /**
+      * Gets the list of SAS related items in the local authority.  We exlude items with the "proposed" flags because
+      * we want a list with only SAS created items.
+      * 
+      * We need to add pagination support to this call!!!
+      * 
+      * @param ctx
+      * @param specifier
+      * @return
+      * @throws Exception
+      */
+     private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier specifier) throws Exception {
+       ArrayList<String> result = new ArrayList<String>();
+       
+       ResourceMap resourceMap = ctx.getResourceMap();
+       String resourceName = ctx.getClient().getServiceName();
+       AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
+       AbstractCommonList acl = authorityResource.getAuthorityItemList(ctx, specifier.value, ctx.getUriInfo());
+       List<ListItem> listItemList = acl.getListItem();
+       for (ListItem listItem:listItemList) {
+               Boolean proposed = getBooleanValue(listItem, AuthorityItemJAXBSchema.PROPOSED);
+               if (proposed == false) { // exclude "proposed" (i.e., local-only items)
+                       result.add(AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME));
+               }
+       }
+       
+       return result;
+     }
+     
+     private Boolean getBooleanValue(ListItem listItem, String name) {
+       Boolean result = null;
+       
+               String value = AbstractCommonListUtils.ListItemGetElementValue(listItem, name);
+               if (value != null) {
+                       result = Boolean.valueOf(value);
+               }
+               
+               return result;
+     }
+     
+     private String getStringValue(ListItem listItem, String name) {
+       return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
+     }
      
      /**
       * This is a sync method.
          //
          // Using the payload from the remote server, create a local copy of the item
          //
 -      ResourceMap resourceMap = ctx.getResourceMap();
 -      String resourceName = ctx.getClient().getServiceName();
 -      AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
 -      Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.value,
 +      AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
 +      Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.getURNValue(),
-                       sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV);
+                       sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED);
        //
        // Check the response for successful POST result
        //
        if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
                throw new DocumentException(String.format("Could not create new authority item '%s' during synchronization of the '%s' authority.",
                                itemIdentifier, parentIdentifier));
+       }
        //
 -      // Handle the workflow state by "locking" this new copy of the SAS item.
 +      // Handle the workflow state
        //
 -    }
+       authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier, 
+                       WorkflowClient.WORKFLOWTRANSITION_LOCK, AuthorityServiceUtils.DONT_UPDATE_REV);
-     }
 +      }
      
      /**
       * Try to synchronize a remote item (using its refName) with a local item.  If the local doesn't yet
                return 1; // exit with status of 1 means we created a new authority item
        }
        //
 -      // If we get here, we know the item exists both locally and remotely, so we need to synchronize them.
 -      // We synchronize by calling the authority resource (the JAX-RS resource) class' sync method for an item.
 +      // If we get here, we know the item exists both locally and remotely, so we need to synchronize them
 +      //
-       PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithParentContext(ctx, parentIdentifier, itemIdentifier);
-       if (theUpdate != null) {
-               result = 0; // means we needed to sync this item with SAS
-               logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS.  Updated payload is: \n%s",
-                               parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
+       //
 -      PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithParentContext(ctx, parentIdentifier, itemIdentifier);
 -      if (theUpdate != null) {
 -              result = 0; // mean we neeed to sync this item with SAS
 -              logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS.  Updated payload is: \n%s",
 -                              parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
++      try {
++              PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
++              if (theUpdate != null) {
++                      result = 0; // means we needed to sync this item with SAS
++                      logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS.  Updated payload is: \n%s",
++                                      parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
++              }
++      } catch (DocumentException de) {
++              cow();
        }
        
        return result; // -1 = no sync needed, 0 = sync'd, 1 = created new item
      }
          
--    private PoxPayloadIn getPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
++    private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
        PoxPayloadIn result = null;
        
          AuthorityClient client = (AuthorityClient) ctx.getClient();
--        Response res = client.readItemList(specifier.value,
++        Response res = client.readItemList(specifier.getURNValue(),
                        null,   // partial term string
                        null    // keyword string
                        );
index 58c28c05024abed45d088d19dce1474b1e56c598,679038fc6e10979bf9a54de6467acc566d6cb1ba..95804a67b7b78da8dbcfe275e5852d6c197d3126
@@@ -40,6 -42,6 +42,7 @@@ import org.collectionspace.services.com
  import org.collectionspace.services.common.document.DocumentException;
  import org.collectionspace.services.common.document.DocumentFilter;
  import org.collectionspace.services.common.document.DocumentNotFoundException;
++import org.collectionspace.services.common.document.DocumentReferenceException;
  import org.collectionspace.services.common.document.DocumentWrapper;
  import org.collectionspace.services.common.repository.RepositoryClient;
  import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema;
@@@ -359,10 -377,12 +378,16 @@@ public abstract class AuthorityItemDocu
        if (this.getShouldUpdateRevNumber() == true) { // We don't update the rev number of synchronization requests
                updateRevNumbers(wrapDoc);
        }
++      
++      DocumentModel docModel = wrapDoc.getWrappedObject();
++      if (this.hasReferencingObjects(this.getServiceContext(), docModel) == true) {
++              
++      }
      }
          
      /**
 -     * This method synchronizes/updates a single authority item resource with the corresponding SAS items.
 -     * The argument 'wrapDoc' in most CSpace methods usually contains a resource payload -like a nuxeo document model.  However,
 +     * This method synchronizes/updates a single authority item resource.
+      * for the handleSync method, the wrapDoc argument contains a authority item specifier.
       */
      @Override
      public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
                                authorityItemSpecifier.getItemSpecifier().value));
          }
          Long localItemRev = (Long) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.REV);
+         Boolean localIsProposed = (Boolean) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.PROPOSED);
          String localItemCsid = itemDocModel.getName();
 -        String localItemWorkflowState = (String) NuxeoUtils.getProperyValue(itemDocModel, CollectionSpaceClient.CORE_WORKFLOWSTATE);
 +        String localItemWorkflowState = itemDocModel.getCurrentLifeCycleState();
          String itemShortId = (String) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
          
          //
          Long sasRev = getRevision(sasPayloadIn);
          String sasWorkflowState = getWorkflowState(sasPayloadIn);
          //
 -        // Update the local item if the shared authority item is newer or if the local item is a proposed item that is now becoming a true SAS item
 +        // If the shared authority item is newer, update our local copy
          //
-         if (sasRev > localItemRev) {
-               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
+         if (sasRev > localItemRev || localIsProposed) {
 -              ResourceMap resourceMap = ctx.getResourceMap();
 -              String resourceName = this.getAuthorityServicePath();
 -              AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
++              AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath());
                PoxPayloadOut payloadOut = authorityResource.updateAuthorityItem(ctx, 
 -                              resourceMap,                                    
 +                              ctx.getResourceMap(),                                   
                                ctx.getUriInfo(),
 -                              localParentCsid,                                                // parent's CSID
 -                              localItemCsid,                                                  // item's CSID
 -                              sasPayloadIn,                                                   // the payload from the remote SAS
 +                              localParentCsid,                                // parent's CSID
 +                              localItemCsid,                                  // item's CSID
 +                              sasPayloadIn,                                   // the payload from the remote SAS
-                               AuthorityServiceUtils.DONT_UPDATE_REV); // don't update the parent's revision number
+                               AuthorityServiceUtils.DONT_UPDATE_REV,  // don't update the parent's revision number
+                               AuthorityServiceUtils.NOT_PROPOSED);    // The items is not proposed, make it a real SAS item now
                if (payloadOut != null) {
                        ctx.setOutput(payloadOut);
                        result = true;
          // If the workflow states are different, we need to update the local's to reflects the remote's
          //
          if (localItemWorkflowState.equalsIgnoreCase(sasWorkflowState) == false) {
-               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
 -              ResourceMap resourceMap = ctx.getResourceMap();
 -              String resourceName = this.getAuthorityServicePath();
 -              AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
++              AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath()); // Get the authority (parent) client not the item client
                //
 -              // We need to move the local item to the SAS workflow state -i.e., the locked state.
 +              // We need to move the local item to the SAS workflow state.  This might involve multiple transitions.
                //
                List<String> transitionList = getTransitionList(sasWorkflowState, localItemWorkflowState);
                for (String transition:transitionList) {
++                      if (transition.equalsIgnoreCase(WorkflowClient.WORKFLOWTRANSITION_DELETE) == true) {
++                              if (hasReferencingObjects(ctx, itemDocModel)) {
++                                      throw new DocumentReferenceException(String.format("Cannot soft-delete authority item '%s' because it still has records in the system that are referencing it.  See the service layer log file for details.",
++                                                      itemDocModel.getName()));
++                              }
++                      }
                        authorityResource.updateItemWorkflowWithTransition(ctx, localParentCsid, localItemCsid, transition, AuthorityServiceUtils.DONT_UPDATE_REV);
                }
 +              result = true;
          }
          
          return result;
       */
      private List<String> getTransitionList(String sasWorkflowState, String localItemWorkflowState) {
        List<String> result = new ArrayList<String>();
-       // TO BE COMPLETELED
++      
+       //
+       // If the SAS authority items is soft-deleted, we need to mark the local item as soft-deleted
+       //
 -      if (sasWorkflowState.contains(WorkflowClient.WORKFLOWSTATE_DELETED) == true) {
++      if (sasWorkflowState.contains(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+               result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
++      } else if (sasWorkflowState.equalsIgnoreCase(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
++              result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+       }
++      
+       //
 -      // Ensure the local item is in a "locked" state.  Items sync'd with a SAS should always be locked
++      // Ensure the local item is always in a "locked" state.  Items sync'd with a SAS should always be locked
+       //
 -      if (localItemWorkflowState.contains(WorkflowClient.WORKFLOWSTATE_LOCKED) != true) {
++      if (localItemWorkflowState.contains(WorkflowClient.WORKFLOWSTATE_LOCKED) != true) { // REM - This may be a bad assumption to make.
+               result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
+       }
        return result;
      }
      
          // Ensure we have required fields set properly
          handleInAuthority(wrapDoc.getWrappedObject());        
      }
 -    
 -    /* This method ensures that before we delete an authority item, there are no records in the system that reference it.
 +
++    /*
++     * This method gets called after the primary update to an authority item has happened.  If the authority item's refName
++     * has changed, then we need to updated all the records that use that refname with the new/updated version
+      * 
+      * (non-Javadoc)
 -     * @see org.collectionspace.services.common.document.DocumentHandler#handleDelete(org.collectionspace.services.common.document.DocumentWrapper)
+      */
+     @Override
+     public void handleDelete(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
 -        UriTemplateRegistry uriTemplateRegistry = ServiceMain.getInstance().getUriTemplateRegistry();
+       ServiceContext ctx = getServiceContext();
+       DocumentModel docModel = wrapDoc.getWrappedObject();
++      
++      if (hasReferencingObjects(ctx, docModel) == true) {
++              throw new DocumentReferenceException(String.format("Cannot delete authority item '%s' because it still has records in the system that are referencing it.  See the service layer log file for details.",
++                              docModel.getName()));
++      }
++    }
++    
++    /**
++     * Checks to see if an authority item has referencing objects.
++     * 
++     * @param ctx
++     * @param docModel
++     * @return
++     * @throws Exception
++     */
++    private boolean hasReferencingObjects(ServiceContext ctx, DocumentModel docModel) throws Exception {
++      boolean result = false;
++      
+       String inAuthorityCsid = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
++      AuthorityResource authorityResource = (AuthorityResource)ctx.getResource(getAuthorityServicePath());
+       String itemCsid = docModel.getName();
 -      
 -      ResourceMap resourceMap = ctx.getResourceMap();
 -      String resourceName = this.getAuthorityServicePath();
 -      AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
 -        //
 -      // If the authority item has references to it from other resources, we can't and won't
 -      // delete it.
 -      //
++        UriTemplateRegistry uriTemplateRegistry = ServiceMain.getInstance().getUriTemplateRegistry();
+       AuthorityRefDocList refObjs = authorityResource.getReferencingObjects(ctx, inAuthorityCsid, itemCsid, 
+                       uriTemplateRegistry, ctx.getUriInfo());
 -      if (refObjs.getTotalItems() != 0) {
 -              throw new DocumentException(String.format("Can not delete authority item '%s' because it still has records in the system that are referencing it.",
 -                              itemCsid));
++      
++      if (refObjs.getTotalItems() > 0) {
++              result = true;
++              logger.error(String.format("Cannot delete authority item '%s' because it still has %d records in the system that are referencing it.",
++                              itemCsid, refObjs.getTotalItems()));
++              logReferencingObjects(docModel, refObjs);
+       }
++      
++      return result;
++    }
++    
++    private void logReferencingObjects(DocumentModel docModel, AuthorityRefDocList refObjs) {
++      List<AuthorityRefDocList.AuthorityRefDocItem> items = refObjs.getAuthorityRefDocItem();
++      int i = 0;
++      for (AuthorityRefDocList.AuthorityRefDocItem item : items) {
++            logger.debug(docModel.getName() + ": list-item[" + i + "] "
++                    + item.getDocType() + "("
++                    + item.getDocId() + ") Name:["
++                    + item.getDocName() + "] Number:["
++                    + item.getDocNumber() + "] in field:["
++                    + item.getSourceField() + "]");
++        }
+     }
      /*
       * This method gets called after the primary update to an authority item has happened.  If the authority item's refName
       * has changed, then we need to updated all the records that use that refname with the new/updated version
                  AuthorityItemJAXBSchema.IN_AUTHORITY, inAuthority);
      }
      
++    /**
++     * Returns a list of records that reference this authority item
++     * 
++     * @param ctx
++     * @param uriTemplateRegistry
++     * @param serviceTypes
++     * @param propertyName
++     * @param itemcsid
++     * @return
++     * @throws Exception
++     */
      public AuthorityRefDocList getReferencingObjects(
                ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
                  UriTemplateRegistry uriTemplateRegistry, 
index f3e99be9f2d1311f129018dcd6146b23ba7fd5c9,f3e99be9f2d1311f129018dcd6146b23ba7fd5c9..815bf5f3e29a55714f4d0c8f0f3d181ac2dbb27f
@@@ -134,7 -134,7 +134,7 @@@ public class CreateAndLinkLoanOutBatchJ
  
                // First, create the Loanout
                // We fetch the resource class by service name
--              NuxeoBasedResource resource = resourceMap.get( LoanoutClient.SERVICE_NAME); 
++              NuxeoBasedResource resource = (NuxeoBasedResource) resourceMap.get( LoanoutClient.SERVICE_NAME); 
                Response response = resource.create(resourceMap, null, loanoutPayload);
                if(response.getStatus() != CREATED_STATUS) {
                        completionStatus = STATUS_ERROR;
                        +   "<relationshipType>"+RELATION_TYPE+"</relationshipType>"
                        +   "<predicateDisplayName>"+RELATION_PREDICATE_DISP+"</predicateDisplayName>"
                        + "</ns2:relations_common></document>";
--              NuxeoBasedResource resource = resourceMap.get(RelationClient.SERVICE_NAME);
++              NuxeoBasedResource resource = (NuxeoBasedResource) resourceMap.get(RelationClient.SERVICE_NAME);
                Response response = resource.create(resourceMap, null, relationPayload);
                if(response.getStatus() != CREATED_STATUS) {
                        completionStatus = STATUS_ERROR;
index 0f2dddc9b0c77113c7d2adfa004140389eb81eb4,0f2dddc9b0c77113c7d2adfa004140389eb81eb4..8475847f2514c4f74b747873ca7644b9d357da38
@@@ -133,8 -133,8 +133,8 @@@ public class UpdateObjectLocationBatchJ
  
      private InvocationResults updateComputedCurrentLocations(List<String> csids) {
          ResourceMap resourcemap = getResourceMap();
--        NuxeoBasedResource collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
--        NuxeoBasedResource movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
++        NuxeoBasedResource collectionObjectResource = (NuxeoBasedResource) resourcemap.get(CollectionObjectClient.SERVICE_NAME);
++        NuxeoBasedResource movementResource = (NuxeoBasedResource) resourcemap.get(MovementClient.SERVICE_NAME);
          String computedCurrentLocation;
          int numUpdated = 0;
  
      // UC Berkeley Botanical Garden v2.4 implementation.
      // #################################################################
      protected PoxPayloadOut findByCsid(String serviceName, String csid) throws URISyntaxException, DocumentException {
--        NuxeoBasedResource resource = getResourceMap().get(serviceName);
++        NuxeoBasedResource resource = (NuxeoBasedResource) getResourceMap().get(serviceName);
          return findByCsid(resource, csid);
      }
  
  
      private List<String> getMemberCsidsFromGroup(String serviceName, String groupCsid) throws URISyntaxException, DocumentException {
          ResourceMap resourcemap = getResourceMap();
--        NuxeoBasedResource resource = resourcemap.get(serviceName);
++        NuxeoBasedResource resource = (NuxeoBasedResource) resourcemap.get(serviceName);
          return getMemberCsidsFromGroup(resource, groupCsid);
      }
  
  
      private List<String> getNoContextCsids() throws URISyntaxException {
          ResourceMap resourcemap = getResourceMap();
--        NuxeoBasedResource collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
++        NuxeoBasedResource collectionObjectResource = (NuxeoBasedResource) resourcemap.get(CollectionObjectClient.SERVICE_NAME);
          UriInfo uriInfo = createUriInfo();
          uriInfo = addFilterToExcludeSoftDeletedRecords(uriInfo);
          AbstractCommonList collectionObjects = collectionObjectResource.getList(uriInfo);
index e342d5ba41741f198358501a5e9c630c8aae61f9,e342d5ba41741f198358501a5e9c630c8aae61f9..212277d30564d1078b19b83834b15674130febd6
@@@ -40,7 -40,7 +40,7 @@@ public abstract class PoxPayload<PT ext
        /** The xml text. */
        private String xmlPayload;
        
--      private Document domDocument;
++      protected Document domDocument;
        
        /** The payload name. */
        private String payloadName;
index 8cd0b4d325f828c3918ba25c7c8cffe1b2098dc8,8cd0b4d325f828c3918ba25c7c8cffe1b2098dc8..83583b4ed900ababfa54bacc2fa5e7ecc133610a
@@@ -68,6 -68,6 +68,14 @@@ public class PoxPayloadOut extends PoxP
                return new PayloadOutputPart(label, element);
        }
        
++      /**
++       * A synonym for the toXML() method.
++       * @return
++       */
++      public String asXML() {
++              return toXML();
++      }
++      
        /**
         * Creates and returns an XML string representation of ourself.
         *
         */
        public String toXML() {
                String result = null;
++        Document document = createDOMFromParts();
++
++        result = document.asXML();
++              
++              if (logger.isTraceEnabled() == true) {
++                      logger.trace("\n\n<<<< Payload Out : BEGIN <<<<\n" + result + "\n<<<< Payload Out : END   <<<<\n");
++              }
++              
++              return result;
++      }
++      
++      private Document createDOMFromParts() {
++              Document result = null;
++              
          Document document = DocumentHelper.createDocument();
          document.setXMLEncoding("UTF-8");
          document.setName(getName());
                                //Add if (logger.isTraceEnabled() == true) logger.trace("Output part: " + outPart.getLabel() + " was empty.");
                        }
                }
--              result = document.asXML();
++              result = document;
++                              
++              return result;
++      }
                
--              if (logger.isTraceEnabled() == true) {
--                      logger.trace("\n\n<<<< Payload Out : BEGIN <<<<\n" +
--                                      result +
--                                      "\n<<<< Payload Out : END   <<<<\n");
++      /**
++       * Gets the DOM object that we created at init time.  This should never be null;
++       *
++       * @return the dOM document
++       */
++      @Override
++      public Document getDOMDocument() {
++              if (domDocument == null) {
++                      domDocument = createDOMFromParts();
                }
                
--              return result;
++              return this.domDocument;
        }
--              
++      
        /**
         * Adds the part.
         *
index db4da7375d50b1108663e943feef30197013e4ce,b8db13132a3d36183f26b9f8c1b38d53353ac65c..d84be81faae78ffe07fb027c2dd2bf52586c0e54
@@@ -24,7 -24,7 +24,9 @@@
  package org.collectionspace.services.common;
  
  import java.lang.reflect.Method;
++import java.util.HashMap;
  import java.util.List;
++import java.util.Map;
  
  import javax.ws.rs.GET;
  import javax.ws.rs.Path;
@@@ -35,6 -35,6 +37,7 @@@ import javax.ws.rs.core.UriInfo
  
  import org.collectionspace.services.common.CSWebApplicationException;
  import org.collectionspace.services.common.api.Tools;
++import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
  import org.collectionspace.services.common.context.ServiceContext;
  import org.collectionspace.services.common.context.ServiceContextProperties;
  import org.collectionspace.services.common.document.BadRequestException;
@@@ -47,6 -47,6 +50,7 @@@ import org.collectionspace.services.com
  import org.collectionspace.services.common.security.UnauthorizedException;
  import org.collectionspace.services.common.storage.StorageClient;
  import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
++import org.collectionspace.services.config.service.ServiceBindingType;
  import org.jboss.resteasy.core.ResourceMethodInvoker;
  //import org.jboss.resteasy.core.ResourceMethod;
  import org.jboss.resteasy.spi.HttpRequest;
@@@ -507,5 -507,5 +511,103 @@@ public abstract class AbstractCollectio
        public boolean allowAnonymousAccess(HttpRequest request,
                        Class<?> resourceClass) {
                return false;
--      }    
++      }
++      
++    /**
++     * Returns a UriRegistry entry: a map of tenant-qualified URI templates
++     * for the current resource, for all tenants
++     * 
++     * @return a map of URI templates for the current resource, for all tenants
++     */
++    public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
++        Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
++                new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
++        List<String> tenantIds = getTenantBindingsReader().getTenantIds();
++        for (String tenantId : tenantIds) {
++                uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getDocType(tenantId), UriTemplateFactory.RESOURCE));
++        }
++        return uriRegistryEntriesMap;
++    }
++    
++    /**
++     * Returns a resource's document type.
++     * 
++     * @param tenantId
++     * @return
++     */
++    @Override
++    public String getDocType(String tenantId) {
++        return getDocType(tenantId, getServiceName());
++    }
++
++    /**
++     * Returns the document type associated with a specified service, within a specified tenant.
++     * 
++     * @param tenantId a tenant ID
++     * @param serviceName a service name
++     * @return the Nuxeo document type associated with that service and tenant.
++     */
++    // FIXME: This method may properly belong in a different services package or class.
++    // Also, we need to check for any existing methods that may duplicate this one.
++    protected String getDocType(String tenantId, String serviceName) {
++        String docType = "";
++        if (Tools.isBlank(tenantId)) {
++            return docType;
++        }
++        ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, serviceName);
++        if (sb == null) {
++            return docType;
++        }
++        docType = sb.getObject().getName(); // Reads the Document Type from tenant bindings configuration
++        return docType;
++    }
++
++      /**
++     * Returns a UriRegistry entry: a map of tenant-qualified URI templates
++     * for the current resource, for a specified tenants
++     * 
++     * @return a map of URI templates for the current resource, for a specified tenant
++     */
++    @Override
++    public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries(String tenantId,
++            String docType, UriTemplateFactory.UriTemplateType type) {
++        Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
++                new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
++        UriTemplateRegistryKey key;
++        if (Tools.isBlank(tenantId) || Tools.isBlank(docType)) {
++            return uriRegistryEntriesMap;
++        }
++        key = new UriTemplateRegistryKey();
++        key.setTenantId(tenantId);
++        key.setDocType(docType); 
++        uriRegistryEntriesMap.put(key, getUriTemplate(type));
++        return uriRegistryEntriesMap;
++    }
++    
++    /**
++     * Returns a URI template of the appropriate type, populated with the
++     * current service name as one of its stored values.
++     *      * 
++     * @param type a URI template type
++     * @return a URI template of the appropriate type.
++     */
++    @Override
++    public StoredValuesUriTemplate getUriTemplate(UriTemplateFactory.UriTemplateType type) {
++        Map<String,String> storedValuesMap = new HashMap<String,String>();
++        storedValuesMap.put(UriTemplateFactory.SERVICENAME_VAR, getServiceName());
++        StoredValuesUriTemplate template =
++                UriTemplateFactory.getURITemplate(type, storedValuesMap);
++        return template;
++    }
++
++    /**
++     * Returns a reader for reading values from tenant bindings configuration
++     * 
++     * @return a tenant bindings configuration reader
++     */
++    @Override
++    public TenantBindingConfigReaderImpl getTenantBindingsReader() {
++        return ServiceMain.getInstance().getTenantBindingConfigReader();
++    }
++      
  }
index 41b6be8108be0eef139ccd5082154b28f5113442,41b6be8108be0eef139ccd5082154b28f5113442..f91d0c6983d50e48260828e5b1b07b168e71f453
@@@ -24,7 -24,7 +24,9 @@@
  package org.collectionspace.services.common;
  
  import java.lang.reflect.Method;
++import java.util.Map;
  
++import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
  import org.collectionspace.services.common.context.ServiceContext;
  import org.collectionspace.services.common.context.ServiceContextFactory;
  import org.collectionspace.services.common.document.DocumentHandler;
@@@ -98,5 -98,5 +100,45 @@@ public interface CollectionSpaceResourc
  //                    <sec:filter-chain pattern="/publicitems/*/*/content"
  //                              filters="none"/>
        public boolean allowAnonymousAccess(HttpRequest request, Class<?> resourceClass);
++      
++    /**
++     * Returns a UriRegistry entry: a map of tenant-qualified URI templates
++     * for the current resource, for all tenants
++     * 
++     * @return a map of URI templates for the current resource, for all tenants
++     */
++    public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries();
      
++    /**
++     * Returns a UriRegistry entry: a map of tenant-qualified URI templates
++     * for the current resource, for a specified tenants
++     * 
++     * @return a map of URI templates for the current resource, for a specified tenant
++     */
++    public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries(String tenantId,
++            String docType, UriTemplateFactory.UriTemplateType type);
++    
++    /**
++     * Returns a URI template of the appropriate type, populated with the
++     * current service name as one of its stored values.
++     *      * 
++     * @param type a URI template type
++     * @return a URI template of the appropriate type.
++     */
++    public StoredValuesUriTemplate getUriTemplate(UriTemplateFactory.UriTemplateType type);
++
++    /**
++     * Returns a reader for reading values from tenant bindings configuration
++     * 
++     * @return a tenant bindings configuration reader
++     */
++    public TenantBindingConfigReaderImpl getTenantBindingsReader();
++
++    /**
++     * Returns the document type of the resource based on the tenant bindings.
++     * 
++     * @param tenantId
++     * @return
++     */
++      public String getDocType(String tenantId);
  }
index f6c7d81cf7ee49a18adb64186247a1736c47f28b,acc9bb8b741e0d0499f0325a8d1dbc4e35e55e1f..18e43053d9198ee6bb8ae8caf5c428aed3125261
@@@ -630,92 -630,92 +630,4 @@@ public abstract class NuxeoBasedResourc
                        throws Exception, DocumentNotFoundException {
        return getDocModelForAuthorityItem(repoSession, RefName.AuthorityItem.parse(refName));
      }
--    
--    protected String getDocType(String tenantId) {
--        return getDocType(tenantId, getServiceName());
--    }
--
--    /**
--     * Returns the Nuxeo document type associated with a specified service, within a specified tenant.
--     * 
--     * @param tenantId a tenant ID
--     * @param serviceName a service name
--     * @return the Nuxeo document type associated with that service and tenant.
--     */
--    // FIXME: This method may properly belong in a different services package or class.
--    // Also, we need to check for any existing methods that may duplicate this one.
--    protected String getDocType(String tenantId, String serviceName) {
--        String docType = "";
--        if (Tools.isBlank(tenantId)) {
--            return docType;
--        }
--        ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, serviceName);
--        if (sb == null) {
--            return docType;
--        }
--        docType = sb.getObject().getName(); // Reads the Nuxeo Document Type from tenant bindings configuration
--        return docType;
--    }
--    
--    /**
--     * Returns a UriRegistry entry: a map of tenant-qualified URI templates
--     * for the current resource, for all tenants
--     * 
--     * @return a map of URI templates for the current resource, for all tenants
--     */
--    public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
--        Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
--                new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
--        List<String> tenantIds = getTenantBindingsReader().getTenantIds();
--        for (String tenantId : tenantIds) {
--                uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getDocType(tenantId), UriTemplateFactory.RESOURCE));
--        }
--        return uriRegistryEntriesMap;
--    }
--    
--    /**
--     * Returns a UriRegistry entry: a map of tenant-qualified URI templates
--     * for the current resource, for a specified tenants
--     * 
--     * @return a map of URI templates for the current resource, for a specified tenant
--     */
--    protected Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries(String tenantId,
--            String docType, UriTemplateFactory.UriTemplateType type) {
--        Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
--                new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
--        UriTemplateRegistryKey key;
--        if (Tools.isBlank(tenantId) || Tools.isBlank(docType)) {
--            return uriRegistryEntriesMap;
--        }
--        key = new UriTemplateRegistryKey();
--        key.setTenantId(tenantId);
--        key.setDocType(docType); 
--        uriRegistryEntriesMap.put(key, getUriTemplate(type));
--        return uriRegistryEntriesMap;
--    }
--    
--    /**
--     * Returns a URI template of the appropriate type, populated with the
--     * current service name as one of its stored values.
--     *      * 
--     * @param type a URI template type
--     * @return a URI template of the appropriate type.
--     */
--    protected StoredValuesUriTemplate getUriTemplate(UriTemplateFactory.UriTemplateType type) {
--        Map<String,String> storedValuesMap = new HashMap<String,String>();
--        storedValuesMap.put(UriTemplateFactory.SERVICENAME_VAR, getServiceName());
--        StoredValuesUriTemplate template =
--                UriTemplateFactory.getURITemplate(type, storedValuesMap);
--        return template;
--    }
--
--    /**
--     * Returns a reader for reading values from tenant bindings configuration
--     * 
--     * @return a tenant bindings configuration reader
--     */
--    protected TenantBindingConfigReaderImpl getTenantBindingsReader() {
--        return ServiceMain.getInstance().getTenantBindingConfigReader();
--    }
--    
  }
index a0869793777ff9060a238db94129cd80d3c2dc74,e74ed266d12aa559ad81ac7b338d4c4d53b104a5..d11f69c736a642dfefb157a6cfb6fbabb61870b9
@@@ -5,9 -5,6 +5,6 @@@ import java.util.Map
  /*
   * Maps service names to Resource instances. Use the Service Client Class to get the service name. 
   */
- public interface ResourceMap extends Map<String, CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut>> {
 -public interface ResourceMap extends Map<String, CollectionSpaceResource> {
++public interface ResourceMap<IT, OT> extends Map<String, CollectionSpaceResource<IT, OT>> {
  
  }
index ee9dc3036f7ffd0815e37469bd53147edb412909,ee9dc3036f7ffd0815e37469bd53147edb412909..85407e90438dbe4870623856dc4b506e13e1802e
@@@ -2,6 -2,6 +2,14 @@@ package org.collectionspace.services.co
  
  import java.util.HashMap;
  
--public class ResourceMapImpl extends HashMap<String, NuxeoBasedResource> implements ResourceMap {
++import org.collectionspace.services.client.PoxPayloadIn;
++import org.collectionspace.services.client.PoxPayloadOut;
++
++public class ResourceMapImpl extends HashMap<String, CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut>> implements ResourceMap<PoxPayloadIn, PoxPayloadOut> {
++
++      /**
++       * 
++       */
++      private static final long serialVersionUID = 1L;
  
  }
index 427a87e5ca1b7ecf955ac6f6b07ba721eeef72e6,427a87e5ca1b7ecf955ac6f6b07ba721eeef72e6..9d59d4f576d17f276cc960f82028d1d1152683c1
@@@ -771,15 -771,15 +771,15 @@@ public class ServiceMain 
       */
      private synchronized void populateUriTemplateRegistry() {
         if (uriTemplateRegistry.isEmpty()) {
--            NuxeoBasedResource resource = null;
++         CollectionSpaceResource resource = null;
              ResourceMap resourceMap = getJaxRSResourceMap();
--            for (Map.Entry<String, NuxeoBasedResource> entry : resourceMap.entrySet()) {
++            Set<Map.Entry<String, CollectionSpaceResource>> entrySet = resourceMap.entrySet();
++            for (Map.Entry<String, CollectionSpaceResource> entry : entrySet) {
                  resource = entry.getValue();
                  Map<UriTemplateRegistryKey, StoredValuesUriTemplate> entries =
                          resource.getUriRegistryEntries();
                  uriTemplateRegistry.putAll(entries);
              }
--
              // FIXME: Contacts itself should not have an entry in the URI template registry;
              // there should be a Contacts entry in that registry only for use in
              // building URIs for resources that have contacts as a sub-resource
index 425ca3114de236bed8665c0f5ccaf111454fd6df,97322e405ad729a0b7251ecb77e012e241afdd30..7fd703394558bb265b5ab8fb6ecf55969afec9d9
@@@ -180,17 -179,6 +180,28 @@@ public class MultipartServiceContextImp
            }
      }
  
 +    @Override
 +    public CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut> getResource() throws Exception {
 +      CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut> result = null;
 +      
 +      ResourceMap resourceMap = getResourceMap();
 +      String resourceName = getClient().getServiceName();
 +      result = (CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut>) resourceMap.get(resourceName);
 +      
 +      return result;
 +    }
++    
++    @Override
++    public CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut> getResource(String serviceName) throws Exception {
++      CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut> result = null;
++      
++      ResourceMap resourceMap = getResourceMap();
++      String resourceName = serviceName;
++      result = (CollectionSpaceResource<PoxPayloadIn, PoxPayloadOut>) resourceMap.get(resourceName);
++      
++      return result;
++    }    
 +
      /* (non-Javadoc)
       * @see org.collectionspace.services.common.context.RemoteServiceContextImpl#getLocalContext(java.lang.String)
       */
index 8b947d5a54bbdba8d2fcc3b7cfa476932e2db5f2,3ab5372b2e17b17b121df263fb100200863e508b..228d70677e033b69c621ccac17a3e0a48b222fe7
@@@ -213,10 -229,4 +230,17 @@@ public class RemoteServiceContextImpl<I
          ServiceContext ctx = (ServiceContext) ctor.newInstance(getServiceName());
          return ctx;
      }
 +
 +      @Override
 +      public CollectionSpaceResource<IT, OT> getResource() throws Exception {
 +              // TODO Auto-generated method stub
 +              throw new RuntimeException("Unimplemented method.");
 +      }
++
++      @Override
++      public CollectionSpaceResource<IT, OT> getResource(String serviceName)
++                      throws Exception {
++              // TODO Auto-generated method stub
++              throw new RuntimeException("Unimplemented method.");
++      }
  }
index 4c4ec4dc57b0376e1599d79500f71aed2210993a,d971c791bc9ec7b4ccde529a158949f62f475b71..0275bf48420d48f5c91266673c6b0f4109828872
@@@ -30,6 -30,6 +30,8 @@@ import javax.ws.rs.core.MultivaluedMap
  import javax.ws.rs.core.UriInfo;
  
  import org.collectionspace.services.client.CollectionSpaceClient;
++import org.collectionspace.services.client.PoxPayloadIn;
++import org.collectionspace.services.client.PoxPayloadOut;
  import org.collectionspace.services.common.CollectionSpaceResource;
  import org.collectionspace.services.common.ResourceMap;
  import org.collectionspace.services.common.document.DocumentHandler;
@@@ -236,12 -236,11 +238,6 @@@ public interface ServiceContext<IT, OT
       */
      public void setOutput(OT output);
  
--    /**
-      * @return the JAX-RS resource of service for the current context.
-      * @throws Exception 
 -     * 
--     */
-     public CollectionSpaceResource<IT, OT> getResource() throws Exception;
 -    public CollectionSpaceResource getResource();
 -    
      /**
       * @return the map of service names to resource classes.
       */
        public void setRepositoryDomain(RepositoryDomainType repositoryDomain);
  
        public CollectionSpaceClient getClient() throws Exception;
++
++    /**
++     * @return the JAX-RS resource of service for the current context.
++     * @throws Exception 
++     */
++    public CollectionSpaceResource<IT, OT> getResource() throws Exception;
++
++    /**
++     * @return the JAX-RS resource of service for the current context.
++     * @throws Exception 
++     */
++      public CollectionSpaceResource<IT, OT> getResource(
++                      String serviceName) throws Exception;
  }
  
  
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2affc2a4d4fad1285c0eadb3263649e55f6cbe44
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++package org.collectionspace.services.common.document;
++
++public class DocumentReferenceException extends DocumentException {
++    /**
++       * 
++       */
++      private static final long serialVersionUID = 1L;
++      final public static int HTTP_CODE = 500;
++
++    /**
++     * Creates a new instance of <code>DocumentNotFoundException</code> without detail message.
++     */
++    public DocumentReferenceException() {
++        setErrorCode(HTTP_CODE);
++    }
++
++    /**
++     * Constructs an instance of <code>DocumentNotFoundException</code> with the specified detail message.
++     * @param msg the detail message.
++     */
++    public DocumentReferenceException(String msg) {
++        super(msg);
++        setErrorCode(HTTP_CODE);
++    }
++
++    /**
++     * Constructs a new exception with the specified detail message and
++     * cause.  <p>Note that the detail message associated with
++     * <code>cause</code> is <i>not</i> automatically incorporated in
++     * this exception's detail message.
++     *
++     * @param  message the detail message (which is saved for later retrieval
++     *         by the {@link #getMessage()} method).
++     * @param  cause the cause (which is saved for later retrieval by the
++     *         {@link #getCause()} method).  (A <tt>null</tt> value is
++     *         permitted, and indicates that the cause is nonexistent or
++     *         unknown.)
++     * @since  1.4
++     */
++    public DocumentReferenceException(String message, Throwable cause) {
++        super(message, cause);
++        setErrorCode(HTTP_CODE);
++    }
++
++    /**
++     * Constructs a new exception with the specified cause and a detail
++     * message of <tt>(cause==null ? null : cause.toString())</tt> (which
++     * typically contains the class and detail message of <tt>cause</tt>).
++     * This constructor is useful for exceptions that are little more than
++     * wrappers for other throwables (for example, {@link
++     * java.security.PrivilegedActionException}).
++     *
++     * @param  cause the cause (which is saved for later retrieval by the
++     *         {@link #getCause()} method).  (A <tt>null</tt> value is
++     *         permitted, and indicates that the cause is nonexistent or
++     *         unknown.)
++     * @since  1.4
++     */
++    public DocumentReferenceException(Throwable cause) {
++        super(cause);
++        setErrorCode(HTTP_CODE);
++    }
++}
index b0d29fcb30f42f0dd28f7a0f97f2daa6e72a1d83,897b587daba44e76b5579c87267d1de1d04e6166..8215c2a5239552c4936dff311c9613b8c9c155ed
@@@ -83,23 -82,12 +83,22 @@@ import org.collectionspace.services.nux
  public class RefNameServiceUtils {
  
      public static enum SpecifierForm {
--        CSID, URN_NAME
++        CSID, URN_NAME // Either a CSID or a short ID
      };
  
      public static class Specifier {
 +        //
 +        // URN statics for things like urn:cspace:name(grover)
 +        //
 +        final static String URN_PREFIX = "urn:cspace:";
 +        final static int URN_PREFIX_LEN = URN_PREFIX.length();
 +        final static String URN_PREFIX_NAME = "name(";
 +        final static int URN_NAME_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_NAME.length();
 +        final static String URN_PREFIX_ID = "id(";
 +        final static int URN_ID_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_ID.length();
 +      
          public SpecifierForm form;
          public String value;
-         public String urnValue;
  
          public Specifier(SpecifierForm form, String value) {
              this.form = form;