]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
DRYD-765: Normalize whitespace.
authorRay Lee <ray.lee@lyrasis.org>
Thu, 3 Oct 2019 18:24:56 +0000 (11:24 -0700)
committerRay Lee <ray.lee@lyrasis.org>
Fri, 11 Oct 2019 00:12:29 +0000 (17:12 -0700)
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

index 39ad2a5b084ee622dc013604923188baefda0a90..4d30c11e9a7a81b6f765bbc774009c6f7d9f8534 100644 (file)
@@ -30,168 +30,168 @@ import org.slf4j.LoggerFactory;
 
 @SuppressWarnings("rawtypes")
 public class AuthorityServiceUtils {
-    private static final Logger logger = LoggerFactory.getLogger(AuthorityServiceUtils.class);
-    //
-    // Used to keep track if an authority item's is deprecated
-    public static final String DEFAULT_REMOTECLIENT_CONFIG_NAME = "default";
-    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 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 SAS_ITEM = true;
-    public static final Boolean NOT_SAS_ITEM = !SAS_ITEM;
-
-    public static final Boolean NO_CHANGE = null;
-
-    /*
-     * Try to find a named remote client configuration in the current tenant bindings.  If the value of the incoming param 'remoteClientConfigName' is
-     * blank or null, we'll try to find a name in the authority service's bindings.  If we can't find a name there, we'll try using the default name.
-     *
-     * If the incoming param 'remoteClientConfigName' is not null, we'll look through all the named remote client configurations in the tenant's binding
-     * to find the configuration.  If we can't find the named configuration, we'll throw an exception.
-     *
-     * If there are no remote client configurations in the tenant's bindings, we'll throw an exception.
-     */
+       private static final Logger logger = LoggerFactory.getLogger(AuthorityServiceUtils.class);
+       //
+       // Used to keep track if an authority item's is deprecated
+       public static final String DEFAULT_REMOTECLIENT_CONFIG_NAME = "default";
+       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 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 SAS_ITEM = true;
+       public static final Boolean NOT_SAS_ITEM = !SAS_ITEM;
+
+       public static final Boolean NO_CHANGE = null;
+
+       /*
+        * Try to find a named remote client configuration in the current tenant bindings.  If the value of the incoming param 'remoteClientConfigName' is
+        * blank or null, we'll try to find a name in the authority service's bindings.  If we can't find a name there, we'll try using the default name.
+        *
+        * If the incoming param 'remoteClientConfigName' is not null, we'll look through all the named remote client configurations in the tenant's binding
+        * to find the configuration.  If we can't find the named configuration, we'll throw an exception.
+        *
+        * If there are no remote client configurations in the tenant's bindings, we'll throw an exception.
+        */
        public static final RemoteClientConfig getRemoteClientConfig(ServiceContext ctx, String remoteClientConfigName) throws Exception {
-       RemoteClientConfig result = null;
-
-       TenantBindingType tenantBinding = ServiceMain.getInstance().getTenantBindingConfigReader().getTenantBinding(ctx.getTenantId());
-       RemoteClientConfigurations remoteClientConfigurations = tenantBinding.getRemoteClientConfigurations();
-       if (remoteClientConfigurations != null) {
-               if (Tools.isEmpty(remoteClientConfigName) == true) {
-                       // Since the authority instance didn't specify a remote client config name, let's see if the authority type's service bindings specifies one
-                       ServiceBindingType serviceBindingType =
-                                       ServiceMain.getInstance().getTenantBindingConfigReader().getServiceBinding(ctx.getTenantId(), ctx.getServiceName());
-                       remoteClientConfigName = serviceBindingType.getRemoteClientConfigName();
-               }
-               //
-               // If we still don't have a remote client config name, let's use the default value.
-               //
-               if (Tools.isEmpty(remoteClientConfigName) == true) {
-                       remoteClientConfigName = DEFAULT_REMOTECLIENT_CONFIG_NAME;
-               }
-
-               List<RemoteClientConfig> remoteClientConfigList = remoteClientConfigurations.getRemoteClientConfig();
-               for (RemoteClientConfig config : remoteClientConfigList) {
-                       if (config.getName().equalsIgnoreCase(remoteClientConfigName)) {
-                               result = config;
-                               break;
-                       }
-               }
-       } else {
-               String errMsg = String.format("No remote client configurations could be found in the tenant bindings for tenant named '%s'.",
-                               ctx.getTenantName());
-               logger.error(errMsg);
-               throw new Exception(errMsg);
-       }
-
-       if (result == null) {
-               String errMsg = String.format("Could not find a remote client configuration named '%s' in the tenant bindings for tenant named '%s'",
-                               remoteClientConfigName, ctx.getTenantName());
-               logger.error(errMsg);
-               throw new Exception(errMsg);
-       }
-
-       return result;
-    }
-
-    /**
-     * Make a request to the SAS Server for an authority payload.
-     *
-     * @param ctx
-     * @param specifier
-     * @param responseType
-     * @return
-     * @throws Exception
-     */
-    static public PoxPayloadIn requestPayloadInFromRemoteServer(ServiceContext ctx, String remoteClientConfigName, Specifier specifier, Class responseType) throws Exception {
-       PoxPayloadIn result = null;
-
-       RemoteClientConfig remoteClientConfig = getRemoteClientConfig(ctx, remoteClientConfigName);
-        AuthorityClient client = (AuthorityClient) ctx.getClient(remoteClientConfig);
-
-        Response res = client.read(specifier.getURNValue());
-        try {
-               int statusCode = res.getStatus();
-               if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
-                   result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response!
-               } else {
-                       String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'.  Server returned status code %d",
-                                       specifier.getURNValue(), remoteClientConfig.getUrl(), statusCode);
-                       if (logger.isDebugEnabled()) {
-                           logger.debug(errMsg);
-                       }
-                       throw new DocumentException(statusCode, errMsg);
-               }
-        } finally {
-               res.close();
-        }
-
-       return result;
-    }
-
-    //
-    // Makes a call to the remote SAS server for a authority item payload
-    //
-    static public PoxPayloadIn requestPayloadInFromRemoteServer(
-               AuthorityItemSpecifier specifier,
-               String remoteClientConfigName,
-               String serviceName,
-               Class responseType,
-               boolean syncHierarchicalRelationships) throws Exception {
-       PoxPayloadIn result = null;
-
-       ServiceContext authorityCtx = new MultipartServiceContextImpl(serviceName);
-       RemoteClientConfig remoteClientConfig = getRemoteClientConfig(authorityCtx, remoteClientConfigName);
-        AuthorityClient client = (AuthorityClient) authorityCtx.getClient(remoteClientConfig);
-        Response res = client.readItem(specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(),
-                       AuthorityClient.INCLUDE_DELETED_ITEMS, syncHierarchicalRelationships);
-
-        try {
-               int statusCode = res.getStatus();
-               if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
-                   result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response.
-               } else {
-                       String errMsg = String.format("Could not retrieve authority item information for '%s:%s' on remote server '%s'.  Server returned status code %d",
-                                       specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(), remoteClientConfig.getUrl(), statusCode);
-                       if (logger.isDebugEnabled()) {
-                           logger.debug(errMsg);
-                       }
-                       throw new DocumentException(statusCode, errMsg);
-               }
-        } finally {
-               res.close();
-        }
-
-       return result;
-    }
-
-    static public boolean setAuthorityItemDeprecated(ServiceContext ctx,
-               DocumentModel docModel, String authorityItemCommonSchemaName, Boolean flag) throws Exception {
-       boolean result = false;
-
-       docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.DEPRECATED,
-                       new Boolean(flag));
-       CoreSessionInterface repoSession = (CoreSessionInterface) ctx.getCurrentRepositorySession();
-       repoSession.saveDocument(docModel);
-       result = true;
-
-       return result;
-    }
-
-    /*
-     * The domain name part of refnames on SAS may not match that of local refnames, so we need to update all the payload's
-     * refnames with the correct domain name
-     */
+               RemoteClientConfig result = null;
+
+               TenantBindingType tenantBinding = ServiceMain.getInstance().getTenantBindingConfigReader().getTenantBinding(ctx.getTenantId());
+               RemoteClientConfigurations remoteClientConfigurations = tenantBinding.getRemoteClientConfigurations();
+               if (remoteClientConfigurations != null) {
+                       if (Tools.isEmpty(remoteClientConfigName) == true) {
+                               // Since the authority instance didn't specify a remote client config name, let's see if the authority type's service bindings specifies one
+                               ServiceBindingType serviceBindingType =
+                                               ServiceMain.getInstance().getTenantBindingConfigReader().getServiceBinding(ctx.getTenantId(), ctx.getServiceName());
+                               remoteClientConfigName = serviceBindingType.getRemoteClientConfigName();
+                       }
+                       //
+                       // If we still don't have a remote client config name, let's use the default value.
+                       //
+                       if (Tools.isEmpty(remoteClientConfigName) == true) {
+                               remoteClientConfigName = DEFAULT_REMOTECLIENT_CONFIG_NAME;
+                       }
+
+                       List<RemoteClientConfig> remoteClientConfigList = remoteClientConfigurations.getRemoteClientConfig();
+                       for (RemoteClientConfig config : remoteClientConfigList) {
+                               if (config.getName().equalsIgnoreCase(remoteClientConfigName)) {
+                                       result = config;
+                                       break;
+                               }
+                       }
+               } else {
+                       String errMsg = String.format("No remote client configurations could be found in the tenant bindings for tenant named '%s'.",
+                                       ctx.getTenantName());
+                       logger.error(errMsg);
+                       throw new Exception(errMsg);
+               }
+
+               if (result == null) {
+                       String errMsg = String.format("Could not find a remote client configuration named '%s' in the tenant bindings for tenant named '%s'",
+                                       remoteClientConfigName, ctx.getTenantName());
+                       logger.error(errMsg);
+                       throw new Exception(errMsg);
+               }
+
+               return result;
+       }
+
+       /**
+        * Make a request to the SAS Server for an authority payload.
+        *
+        * @param ctx
+        * @param specifier
+        * @param responseType
+        * @return
+        * @throws Exception
+        */
+       static public PoxPayloadIn requestPayloadInFromRemoteServer(ServiceContext ctx, String remoteClientConfigName, Specifier specifier, Class responseType) throws Exception {
+               PoxPayloadIn result = null;
+
+               RemoteClientConfig remoteClientConfig = getRemoteClientConfig(ctx, remoteClientConfigName);
+               AuthorityClient client = (AuthorityClient) ctx.getClient(remoteClientConfig);
+
+               Response res = client.read(specifier.getURNValue());
+               try {
+                       int statusCode = res.getStatus();
+                       if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
+                               result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response!
+                       } else {
+                               String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'.  Server returned status code %d",
+                                               specifier.getURNValue(), remoteClientConfig.getUrl(), statusCode);
+                               if (logger.isDebugEnabled()) {
+                                       logger.debug(errMsg);
+                               }
+                               throw new DocumentException(statusCode, errMsg);
+                       }
+               } finally {
+                       res.close();
+               }
+
+               return result;
+       }
+
+       //
+       // Makes a call to the remote SAS server for a authority item payload
+       //
+       static public PoxPayloadIn requestPayloadInFromRemoteServer(
+                       AuthorityItemSpecifier specifier,
+                       String remoteClientConfigName,
+                       String serviceName,
+                       Class responseType,
+                       boolean syncHierarchicalRelationships) throws Exception {
+               PoxPayloadIn result = null;
+
+               ServiceContext authorityCtx = new MultipartServiceContextImpl(serviceName);
+               RemoteClientConfig remoteClientConfig = getRemoteClientConfig(authorityCtx, remoteClientConfigName);
+               AuthorityClient client = (AuthorityClient) authorityCtx.getClient(remoteClientConfig);
+               Response res = client.readItem(specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(),
+                               AuthorityClient.INCLUDE_DELETED_ITEMS, syncHierarchicalRelationships);
+
+               try {
+                       int statusCode = res.getStatus();
+                       if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
+                               result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response.
+                       } else {
+                               String errMsg = String.format("Could not retrieve authority item information for '%s:%s' on remote server '%s'.  Server returned status code %d",
+                                               specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(), remoteClientConfig.getUrl(), statusCode);
+                               if (logger.isDebugEnabled()) {
+                                       logger.debug(errMsg);
+                               }
+                               throw new DocumentException(statusCode, errMsg);
+                       }
+               } finally {
+                       res.close();
+               }
+
+               return result;
+       }
+
+       static public boolean setAuthorityItemDeprecated(ServiceContext ctx,
+                       DocumentModel docModel, String authorityItemCommonSchemaName, Boolean flag) throws Exception {
+               boolean result = false;
+
+               docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.DEPRECATED,
+                               new Boolean(flag));
+               CoreSessionInterface repoSession = (CoreSessionInterface) ctx.getCurrentRepositorySession();
+               repoSession.saveDocument(docModel);
+               result = true;
+
+               return result;
+       }
+
+       /*
+        * The domain name part of refnames on SAS may not match that of local refnames, so we need to update all the payload's
+        * refnames with the correct domain name
+        */
        static public PoxPayloadIn filterRefnameDomains(ServiceContext ctx, PoxPayloadIn payload) throws org.dom4j.DocumentException {
                PoxPayloadIn result = null;
 
@@ -218,25 +218,25 @@ public class AuthorityServiceUtils {
                return result;
        }
 
-    /**
-     * Mark the authority item as deprecated.
-     *
-     * @param ctx
-     * @param itemInfo
-     * @throws Exception
-     */
-    static public boolean markAuthorityItemAsDeprecated(ServiceContext ctx, String authorityItemCommonSchemaName, AuthorityItemSpecifier authorityItemSpecifier) throws Exception {
-       boolean result = false;
-
-       try {
-               DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(),
-                               authorityItemCommonSchemaName, authorityItemSpecifier);
-               result = setAuthorityItemDeprecated(ctx, docModel, authorityItemCommonSchemaName, AuthorityServiceUtils.DEPRECATED);
-       } catch (Exception e) {
-               logger.warn(String.format("Could not mark item '%s' as deprecated.", authorityItemSpecifier.getItemSpecifier().getURNValue()), e);
-               throw e;
-       }
-
-       return result;
-    }
+       /**
+        * Mark the authority item as deprecated.
+        *
+        * @param ctx
+        * @param itemInfo
+        * @throws Exception
+        */
+       static public boolean markAuthorityItemAsDeprecated(ServiceContext ctx, String authorityItemCommonSchemaName, AuthorityItemSpecifier authorityItemSpecifier) throws Exception {
+               boolean result = false;
+
+               try {
+                       DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(),
+                                       authorityItemCommonSchemaName, authorityItemSpecifier);
+                       result = setAuthorityItemDeprecated(ctx, docModel, authorityItemCommonSchemaName, AuthorityServiceUtils.DEPRECATED);
+               } catch (Exception e) {
+                       logger.warn(String.format("Could not mark item '%s' as deprecated.", authorityItemSpecifier.getItemSpecifier().getURNValue()), e);
+                       throw e;
+               }
+
+               return result;
+       }
 }
index e1022b20d959121ac13e665c630e9827fac5fe55..87f2cdd03c8242d82e7a1e024b8a4c4196deaf7d 100644 (file)
@@ -79,258 +79,258 @@ import org.slf4j.LoggerFactory;
  */
 @SuppressWarnings("rawtypes")
 public abstract class AuthorityDocumentModelHandler<AuthCommon>
-        extends NuxeoDocumentModelHandler<AuthCommon> {
+               extends NuxeoDocumentModelHandler<AuthCommon> {
 
        private final Logger logger = LoggerFactory.getLogger(AuthorityDocumentModelHandler.class);
 
        protected String authorityCommonSchemaName;
-    protected String authorityItemCommonSchemaName;
-    protected boolean shouldUpdateRevNumber = true; // default to updating the revision number
-
-    public AuthorityDocumentModelHandler(String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
-        this.authorityCommonSchemaName = authorityCommonSchemaName;
-        this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
-    }
-
-    public void setShouldUpdateRevNumber(boolean flag) {
-       this.shouldUpdateRevNumber = flag;
-    }
-
-    public boolean getShouldUpdateRevNumber() {
-       return this.shouldUpdateRevNumber;
-    }
-
-    /**
-     * The entity type expected from the JAX-RS Response object
-     */
-    public Class<String> getEntityResponseType() {
-       return String.class;
-    }
-
-    @Override
-    public void prepareSync() throws Exception {
-       this.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV);  // Never update rev nums on sync operations
-    }
-
-    protected PayloadInputPart extractPart(Response res, String partLabel)
-            throws Exception {
-            PoxPayloadIn input = new PoxPayloadIn((String)res.readEntity(getEntityResponseType()));
-            PayloadInputPart payloadInputPart = input.getPart(partLabel);
-            if (payloadInputPart == null) {
-               logger.error("Part " + partLabel + " was unexpectedly null.");
-            }
-            return payloadInputPart;
-    }
+       protected String authorityItemCommonSchemaName;
+       protected boolean shouldUpdateRevNumber = true; // default to updating the revision number
+
+       public AuthorityDocumentModelHandler(String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
+               this.authorityCommonSchemaName = authorityCommonSchemaName;
+               this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
+       }
+
+       public void setShouldUpdateRevNumber(boolean flag) {
+               this.shouldUpdateRevNumber = flag;
+       }
+
+       public boolean getShouldUpdateRevNumber() {
+               return this.shouldUpdateRevNumber;
+       }
+
+       /**
+        * The entity type expected from the JAX-RS Response object
+        */
+       public Class<String> getEntityResponseType() {
+               return String.class;
+       }
+
+       @Override
+       public void prepareSync() throws Exception {
+               this.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV);  // Never update rev nums on sync operations
+       }
+
+       protected PayloadInputPart extractPart(Response res, String partLabel)
+                       throws Exception {
+                       PoxPayloadIn input = new PoxPayloadIn((String)res.readEntity(getEntityResponseType()));
+                       PayloadInputPart payloadInputPart = input.getPart(partLabel);
+                       if (payloadInputPart == null) {
+                               logger.error("Part " + partLabel + " was unexpectedly null.");
+                       }
+                       return payloadInputPart;
+       }
 
        @Override
-    public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
-       boolean result = false;
-       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
-        Specifier specifier = (Specifier) wrapDoc.getWrappedObject();
-        //
-        // Get the rev number of the authority so we can compare with rev number of shared authority
-        //
-        DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, specifier);
-        if (docModel != null) {
-               Long localRev = (Long) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REV);
-               String shortId = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
-               String remoteClientConfigName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME); // If set, contains the name of the remote client configuration (remoteClientConfigName) from the tenant bindings
-               //
-               // Using the short ID of the local authority, create a URN specifier to retrieve the SAS authority
-               //
-               Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, shortId);
-               PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(ctx, remoteClientConfigName, sasSpecifier, getEntityResponseType());
-               //
-               // 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 || true) { // FIXME: Along with the revision number, we need to use other meta information to determine if a sync should happen -for now, alway sync
-                       //
-                       // First, sync all the authority items
-                       //
-                       syncAllItems(ctx, sasSpecifier);  // FIXME: We probably want to consider "paging" this instead of handling the entire set of items.
-                       //
-                       // Next, sync the authority resource/record itself
-                       //
-                       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);
-                               result = true;
-                       }
-                       //
-                       // We may need to transition the authority into a replicated state the first time we sync it.
-                       //
-                       String workflowState = docModel.getCurrentLifeCycleState();
-                       if (workflowState.contains(WorkflowClient.WORKFLOWSTATE_REPLICATED) == false) {
-                           String authorityCsid = docModel.getName();
-                               authorityResource.updateWorkflowWithTransition(ctx, ctx.getUriInfo(), authorityCsid, WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-                       }
-               }
-        } else {
-               String errMsg = String.format("Authority of type '%s' with identifier '%s' does not exist.",
-                               getServiceContext().getServiceName(), specifier.getURNValue());
-               logger.debug(errMsg);
-               throw new DocumentException(errMsg);
-        }
-
-        return result;
-    }
-
-    /*
-     * Get the list of authority items from the remote shared authority server (SAS) and try
-     * to synchronize them with the local items.  If items exist on the remote but not the local, we'll create them.
-     */
-    protected int syncAllItems(ServiceContext ctx, Specifier sasAuthoritySpecifier) throws Exception {
-       int result = -1;
-       int created = 0;
-       int synched = 0;
-       int alreadySynched = 0;
-       int deprecated = 0;
-       int totalItemsProcessed = 0;
-       ArrayList<String> itemsInRemoteAuthority = new ArrayList<String>();
-       //
-       // Iterate over the list of items/terms in the remote authority
-       //
-        PoxPayloadIn sasPayloadInItemList = requestPayloadInItemList(ctx, sasAuthoritySpecifier);
-        List<Element> itemList = getItemList(sasPayloadInItemList);
-        if (itemList != null) {
-               for (Element e:itemList) {
-                       String remoteRefName = XmlTools.getElementValue(e, AuthorityItemJAXBSchema.REF_NAME);
-                       itemsInRemoteAuthority.add(XmlTools.getElementValue(e, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
-                       long status = syncRemoteItemWithLocalItem(ctx, remoteRefName);
-                       if (status == 1) {
-                               created++;
-                       } else if (status == 0) {
-                               synched++;
-                       } else {
-                               alreadySynched++;
-                       }
-                       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, sasAuthoritySpecifier);
-       itemsInLocalAuthority.removeAll(itemsInRemoteAuthority);
-       if (itemsInLocalAuthority.size() > 0) {
-               ArrayList<String> remainingItems = itemsInLocalAuthority; // now a subset of local items that no longer exist on the SAS, so we need to try to delete them (or mark them as deprecated if they still have records referencing them)
-               //
-               // We now need to either hard-delete or deprecate the remaining authorities
-               //
-               long processed = deleteOrDeprecateItems(ctx, sasAuthoritySpecifier, remainingItems);
-               if (processed != remainingItems.size()) {
-                       throw new Exception("Encountered unexpected exception trying to delete or deprecated authority items during synchronization.");
-               }
-       }
-       //
-       // Now that we've sync'd all the items, we need to synchronize the hierarchy relationships
-       //
-       for (String itemShortId:itemsInRemoteAuthority) {
-               long status = syncRemoteItemRelationshipsWithLocalItem(ctx, sasAuthoritySpecifier, itemShortId);
-               if (status == 1) {
-                       created++;
-               } else if (status == 0) {
-                       synched++;
-               } else {
-                       alreadySynched++;
-               }
-               totalItemsProcessed++;
-       }
-
-        logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed));
-        logger.info(String.format("Number of items synchronized: %d", synched));
-        logger.info(String.format("Number of items created during sync: %d", created));
-        logger.info(String.format("Number not needing synchronization: %d", alreadySynched));
-
-        return result;
-    }
-
-    /**
-     * This method should ***only*** be used as part of a SAS synch operation.
-     *
-     * @param ctx
-     * @param refNameList
-     * @return
-     * @throws Exception
-     */
+       public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
+               boolean result = false;
+               ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
+               Specifier specifier = (Specifier) wrapDoc.getWrappedObject();
+               //
+               // Get the rev number of the authority so we can compare with rev number of shared authority
+               //
+               DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, specifier);
+               if (docModel != null) {
+                       Long localRev = (Long) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REV);
+                       String shortId = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
+                       String remoteClientConfigName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME); // If set, contains the name of the remote client configuration (remoteClientConfigName) from the tenant bindings
+                       //
+                       // Using the short ID of the local authority, create a URN specifier to retrieve the SAS authority
+                       //
+                       Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, shortId);
+                       PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(ctx, remoteClientConfigName, sasSpecifier, getEntityResponseType());
+                       //
+                       // 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 || true) { // FIXME: Along with the revision number, we need to use other meta information to determine if a sync should happen -for now, alway sync
+                               //
+                               // First, sync all the authority items
+                               //
+                               syncAllItems(ctx, sasSpecifier);  // FIXME: We probably want to consider "paging" this instead of handling the entire set of items.
+                               //
+                               // Next, sync the authority resource/record itself
+                               //
+                               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);
+                                       result = true;
+                               }
+                               //
+                               // We may need to transition the authority into a replicated state the first time we sync it.
+                               //
+                               String workflowState = docModel.getCurrentLifeCycleState();
+                               if (workflowState.contains(WorkflowClient.WORKFLOWSTATE_REPLICATED) == false) {
+                                       String authorityCsid = docModel.getName();
+                                       authorityResource.updateWorkflowWithTransition(ctx, ctx.getUriInfo(), authorityCsid, WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+                               }
+                       }
+               } else {
+                       String errMsg = String.format("Authority of type '%s' with identifier '%s' does not exist.",
+                                       getServiceContext().getServiceName(), specifier.getURNValue());
+                       logger.debug(errMsg);
+                       throw new DocumentException(errMsg);
+               }
+
+               return result;
+       }
+
+       /*
+        * Get the list of authority items from the remote shared authority server (SAS) and try
+        * to synchronize them with the local items.  If items exist on the remote but not the local, we'll create them.
+        */
+       protected int syncAllItems(ServiceContext ctx, Specifier sasAuthoritySpecifier) throws Exception {
+               int result = -1;
+               int created = 0;
+               int synched = 0;
+               int alreadySynched = 0;
+               int deprecated = 0;
+               int totalItemsProcessed = 0;
+               ArrayList<String> itemsInRemoteAuthority = new ArrayList<String>();
+               //
+               // Iterate over the list of items/terms in the remote authority
+               //
+               PoxPayloadIn sasPayloadInItemList = requestPayloadInItemList(ctx, sasAuthoritySpecifier);
+               List<Element> itemList = getItemList(sasPayloadInItemList);
+               if (itemList != null) {
+                       for (Element e:itemList) {
+                               String remoteRefName = XmlTools.getElementValue(e, AuthorityItemJAXBSchema.REF_NAME);
+                               itemsInRemoteAuthority.add(XmlTools.getElementValue(e, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
+                               long status = syncRemoteItemWithLocalItem(ctx, remoteRefName);
+                               if (status == 1) {
+                                       created++;
+                               } else if (status == 0) {
+                                       synched++;
+                               } else {
+                                       alreadySynched++;
+                               }
+                               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, sasAuthoritySpecifier);
+               itemsInLocalAuthority.removeAll(itemsInRemoteAuthority);
+               if (itemsInLocalAuthority.size() > 0) {
+                       ArrayList<String> remainingItems = itemsInLocalAuthority; // now a subset of local items that no longer exist on the SAS, so we need to try to delete them (or mark them as deprecated if they still have records referencing them)
+                       //
+                       // We now need to either hard-delete or deprecate the remaining authorities
+                       //
+                       long processed = deleteOrDeprecateItems(ctx, sasAuthoritySpecifier, remainingItems);
+                       if (processed != remainingItems.size()) {
+                               throw new Exception("Encountered unexpected exception trying to delete or deprecated authority items during synchronization.");
+                       }
+               }
+               //
+               // Now that we've sync'd all the items, we need to synchronize the hierarchy relationships
+               //
+               for (String itemShortId:itemsInRemoteAuthority) {
+                       long status = syncRemoteItemRelationshipsWithLocalItem(ctx, sasAuthoritySpecifier, itemShortId);
+                       if (status == 1) {
+                               created++;
+                       } else if (status == 0) {
+                               synched++;
+                       } else {
+                               alreadySynched++;
+                       }
+                       totalItemsProcessed++;
+               }
+
+               logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed));
+               logger.info(String.format("Number of items synchronized: %d", synched));
+               logger.info(String.format("Number of items created during sync: %d", created));
+               logger.info(String.format("Number not needing synchronization: %d", alreadySynched));
+
+               return result;
+       }
+
+       /**
+        * This method should ***only*** be used as part of a SAS synch operation.
+        *
+        * @param ctx
+        * @param refNameList
+        * @return
+        * @throws Exception
+        */
        private long deleteOrDeprecateItems(ServiceContext ctx, Specifier authoritySpecifier, ArrayList<String> itemShortIdList) throws Exception {
-       long result = 0;
-        AuthorityItemSpecifier authorityItemSpecificer = null;
-
-        ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, false); // Don't update the revision number when we delete or deprecate the item
-       for (String itemShortId:itemShortIdList) {
-               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
-               try {
-               authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authoritySpecifier.value,
-                               itemShortId);
-                       authorityResource.deleteAuthorityItem(ctx,
-                                       authorityItemSpecificer.getParentSpecifier().getURNValue(),
-                                       authorityItemSpecificer.getItemSpecifier().getURNValue(),
-                                       AuthorityServiceUtils.DONT_UPDATE_REV); // Since we're sync'ing, we shouldn't update the revision number (obviously this only applies to soft-deletes since hard-deletes destroy the record)
-                       result++;
-               } catch (DocumentReferenceException de) {
-                       logger.info(String.format("Authority item with '%s' has existing references and cannot be removed during sync.",
-                                       authorityItemSpecificer), de);
-                       boolean marked = AuthorityServiceUtils.markAuthorityItemAsDeprecated(ctx, authorityItemCommonSchemaName,
-                                       authorityItemSpecificer);
-                       if (marked == true) {
-                               result++;
-                       }
-               } catch (Exception e) {
-                       logger.warn(String.format("Unable to delete authority item '%s'", authorityItemSpecificer), e);
-                       throw e;
-               }
-       }
-
-       if (logger.isWarnEnabled() == true) {
-               if (result != itemShortIdList.size()) {
-                       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, itemShortIdList.size()));
-               }
-       }
-
-       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 authoritySpecifier
-     * @return
-     * @throws Exception
-     */
-    private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier authoritySpecifier) 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, authoritySpecifier.getURNValue(), 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.SHORT_IDENTIFIER));
-               }
-       }
-
-       return result;
-    }
-
-    private Boolean getBooleanValue(ListItem listItem, String name) {
-       Boolean result = null;
+               long result = 0;
+               AuthorityItemSpecifier authorityItemSpecificer = null;
+
+               ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, false); // Don't update the revision number when we delete or deprecate the item
+               for (String itemShortId:itemShortIdList) {
+                       AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
+                       try {
+                               authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authoritySpecifier.value,
+                                               itemShortId);
+                               authorityResource.deleteAuthorityItem(ctx,
+                                               authorityItemSpecificer.getParentSpecifier().getURNValue(),
+                                               authorityItemSpecificer.getItemSpecifier().getURNValue(),
+                                               AuthorityServiceUtils.DONT_UPDATE_REV); // Since we're sync'ing, we shouldn't update the revision number (obviously this only applies to soft-deletes since hard-deletes destroy the record)
+                               result++;
+                       } catch (DocumentReferenceException de) {
+                               logger.info(String.format("Authority item with '%s' has existing references and cannot be removed during sync.",
+                                               authorityItemSpecificer), de);
+                               boolean marked = AuthorityServiceUtils.markAuthorityItemAsDeprecated(ctx, authorityItemCommonSchemaName,
+                                               authorityItemSpecificer);
+                               if (marked == true) {
+                                       result++;
+                               }
+                       } catch (Exception e) {
+                               logger.warn(String.format("Unable to delete authority item '%s'", authorityItemSpecificer), e);
+                               throw e;
+                       }
+               }
+
+               if (logger.isWarnEnabled() == true) {
+                       if (result != itemShortIdList.size()) {
+                               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, itemShortIdList.size()));
+                       }
+               }
+
+               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 authoritySpecifier
+        * @return
+        * @throws Exception
+        */
+       private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier authoritySpecifier) 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, authoritySpecifier.getURNValue(), 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.SHORT_IDENTIFIER));
+                       }
+               }
+
+               return result;
+       }
+
+       private Boolean getBooleanValue(ListItem listItem, String name) {
+               Boolean result = null;
 
                String value = AbstractCommonListUtils.ListItemGetElementValue(listItem, name);
                if (value != null) {
@@ -338,308 +338,308 @@ public abstract class AuthorityDocumentModelHandler<AuthCommon>
                }
 
                return result;
-    }
-
-    private String getStringValue(ListItem listItem, String name) {
-       return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
-    }
-
-    /**
-     * This method should only be used during a SAS synchronization request.
-     *
-     * @param ctx
-     * @param parentIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
-     * @param itemIdentifier   - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
-     * @throws Exception
-     */
-    protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier, Boolean syncHierarchicalRelationships) throws Exception {
-       //
-       // Create a URN short ID specifier for the getting a copy of the remote authority item
-       //
-        Specifier authoritySpecifier = Specifier.getSpecifier(parentIdentifier);
-        Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier);
-        AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(authoritySpecifier, itemSpecifier);
-        //
-        // Get the remote client configuration name
-        //
-        DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, authoritySpecifier);
-        String remoteClientConfigName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME); // If set, contains the name of the remote client configuration (remoteClientConfigName) from the tenant bindings
-        //
-        // Get the remote payload
-        //
-        PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier, remoteClientConfigName,
-                       ctx.getServiceName(), getEntityResponseType(), syncHierarchicalRelationships);
-        sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter domain name part of any and all refnames in the payload
-        //
-        // Using the payload from the remote server, create a local copy of the item
-        //
-       AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
-       Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.getURNValue(),
-                       sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED, AuthorityServiceUtils.SAS_ITEM);
-       //
-       // 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));
-       }
-       //
-       // Since we're creating an item that was sourced from the replication server, we need to replicate it locally.
-       //
-       authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier,
-                       WorkflowClient.WORKFLOWTRANSITION_REPLICATE, AuthorityServiceUtils.DONT_UPDATE_REV); // don't update the rev number of the new replicated item (use the rev number of the sourced item)
-       }
-
-    /**
-     * Try to synchronize a remote item (using its refName) with a local item.  If the local doesn't yet
-     * exist, we'll create it.
-     * Result values:
-     *         -1 = sync not needed; i.e., already in sync
-     *   0 = sync succeeded
-     *   1 = local item was missing so we created it
-     * @param ctx
-     * @param refName
-     * @return
-     * @throws Exception
-     */
-    protected long syncRemoteItemWithLocalItem(ServiceContext ctx, String itemRefName) throws Exception {
-       long result = -1;
-       //
-       // Using the item refname (with no local CSID), create specifiers that we'll use to find the local versions
-       //
-       AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName);
-       String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
-       String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
-       //
-       // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
-       //
-       AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
-       PoxPayloadOut localItemPayloadOut;
-       try {
-               localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
-       } catch (DocumentNotFoundException dnf) {
-               //
-               // Document not found, means we need to create an item/term that exists only on the SAS
-               //
-               logger.info(String.format("Remote item with refname='%s' doesn't exist locally, so we'll create it.", itemRefName));
-               createLocalItem(ctx, parentIdentifier, itemIdentifier, AuthorityClient.DONT_INCLUDE_RELATIONS);
-               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.
-       //
-       //
-       try {
-               PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, false);
-               if (theUpdate != null) {
-                       result = 0; // means we needed to sync this item with SAS
-                       logger.debug(String.format("Synced authority item %s in authority %s",
-                                       itemIdentifier, parentIdentifier));
-               }
-       } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
-               result = -1;
-               logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
-                               itemIdentifier));
-       }
-
-       return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
-    }
-
-    /**
-     * Ensure the local items relationships look the same as the remote items' by synchronizing the hierarchy relationship records
-     * of the SAS item with the local item.
-     *
-     * @param ctx
-     * @param refName
-     * @return
-     * @throws Exception
-     */
-    protected long syncRemoteItemRelationshipsWithLocalItem(ServiceContext ctx, Specifier authoritySpecifier, String itemShortId) throws Exception {
-       long result = -1;
-
-       String parentIdentifier = authoritySpecifier.getURNValue();
-       String itemIdentifier = Specifier.createShortIdURNValue(itemShortId);
-       //
-       // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
-       //
-       AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
-       PoxPayloadOut localItemPayloadOut;
-       try {
-               MultivaluedMap queryParams = ctx.getQueryParams();
-               localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
-       } catch (DocumentNotFoundException dnf) {
-               //
-               // Document not found, means we need to create an item/term that exists only on the SAS
-               //
-               logger.info(String.format("Remote item with short ID ='%s' doesn't exist locally, so we can't synchronize its relationships.", itemShortId));
-               return result;
-       }
-       //
-       // If we get here, we know the item exists both locally and remotely, so we need to synchronize the hierarchy relationships.
-       //
-       //
-       try {
-               PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, AuthorityClient.INCLUDE_RELATIONS);
-               if (theUpdate != null) {
-                       result = 0; // means we needed to sync this item with SAS
-                       logger.debug(String.format("Synced authority item %s in authority %s",
+       }
+
+       private String getStringValue(ListItem listItem, String name) {
+               return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
+       }
+
+       /**
+        * This method should only be used during a SAS synchronization request.
+        *
+        * @param ctx
+        * @param parentIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
+        * @param itemIdentifier   - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
+        * @throws Exception
+        */
+       protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier, Boolean syncHierarchicalRelationships) throws Exception {
+               //
+               // Create a URN short ID specifier for the getting a copy of the remote authority item
+               //
+               Specifier authoritySpecifier = Specifier.getSpecifier(parentIdentifier);
+               Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier);
+               AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(authoritySpecifier, itemSpecifier);
+               //
+               // Get the remote client configuration name
+               //
+               DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, authoritySpecifier);
+               String remoteClientConfigName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME); // If set, contains the name of the remote client configuration (remoteClientConfigName) from the tenant bindings
+               //
+               // Get the remote payload
+               //
+               PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier, remoteClientConfigName,
+                               ctx.getServiceName(), getEntityResponseType(), syncHierarchicalRelationships);
+               sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter domain name part of any and all refnames in the payload
+               //
+               // Using the payload from the remote server, create a local copy of the item
+               //
+               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
+               Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.getURNValue(),
+                               sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED, AuthorityServiceUtils.SAS_ITEM);
+               //
+               // 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));
+               }
+               //
+               // Since we're creating an item that was sourced from the replication server, we need to replicate it locally.
+               //
+               authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier,
+                               WorkflowClient.WORKFLOWTRANSITION_REPLICATE, AuthorityServiceUtils.DONT_UPDATE_REV); // don't update the rev number of the new replicated item (use the rev number of the sourced item)
+               }
+
+       /**
+        * Try to synchronize a remote item (using its refName) with a local item.  If the local doesn't yet
+        * exist, we'll create it.
+        * Result values:
+        *      -1 = sync not needed; i.e., already in sync
+        *   0 = sync succeeded
+        *   1 = local item was missing so we created it
+        * @param ctx
+        * @param refName
+        * @return
+        * @throws Exception
+        */
+       protected long syncRemoteItemWithLocalItem(ServiceContext ctx, String itemRefName) throws Exception {
+               long result = -1;
+               //
+               // Using the item refname (with no local CSID), create specifiers that we'll use to find the local versions
+               //
+               AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName);
+               String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
+               String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
+               //
+               // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
+               //
+               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
+               PoxPayloadOut localItemPayloadOut;
+               try {
+                       localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
+               } catch (DocumentNotFoundException dnf) {
+                       //
+                       // Document not found, means we need to create an item/term that exists only on the SAS
+                       //
+                       logger.info(String.format("Remote item with refname='%s' doesn't exist locally, so we'll create it.", itemRefName));
+                       createLocalItem(ctx, parentIdentifier, itemIdentifier, AuthorityClient.DONT_INCLUDE_RELATIONS);
+                       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.
+               //
+               //
+               try {
+                       PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, false);
+                       if (theUpdate != null) {
+                               result = 0; // means we needed to sync this item with SAS
+                               logger.debug(String.format("Synced authority item %s in authority %s",
+                                               itemIdentifier, parentIdentifier));
+                       }
+               } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
+                       result = -1;
+                       logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
+                                       itemIdentifier));
+               }
+
+               return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
+       }
+
+       /**
+        * Ensure the local items relationships look the same as the remote items' by synchronizing the hierarchy relationship records
+        * of the SAS item with the local item.
+        *
+        * @param ctx
+        * @param refName
+        * @return
+        * @throws Exception
+        */
+       protected long syncRemoteItemRelationshipsWithLocalItem(ServiceContext ctx, Specifier authoritySpecifier, String itemShortId) throws Exception {
+               long result = -1;
+
+               String parentIdentifier = authoritySpecifier.getURNValue();
+               String itemIdentifier = Specifier.createShortIdURNValue(itemShortId);
+               //
+               // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
+               //
+               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
+               PoxPayloadOut localItemPayloadOut;
+               try {
+                       MultivaluedMap queryParams = ctx.getQueryParams();
+                       localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
+               } catch (DocumentNotFoundException dnf) {
+                       //
+                       // Document not found, means we need to create an item/term that exists only on the SAS
+                       //
+                       logger.info(String.format("Remote item with short ID ='%s' doesn't exist locally, so we can't synchronize its relationships.", itemShortId));
+                       return result;
+               }
+               //
+               // If we get here, we know the item exists both locally and remotely, so we need to synchronize the hierarchy relationships.
+               //
+               //
+               try {
+                       PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, AuthorityClient.INCLUDE_RELATIONS);
+                       if (theUpdate != null) {
+                               result = 0; // means we needed to sync this item with SAS
+                               logger.debug(String.format("Synced authority item %s in authority %s",
                                                        itemIdentifier, parentIdentifier));
                                }
-       } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
-               result = -1;
-               logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
-                               itemIdentifier));
-       }
-
-       return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
-
-
-    }
-
-    private void assertStatusCode(Response res, Specifier specifier, AuthorityClient client) throws Exception {
-        int statusCode = res.getStatus();
-
-       if (statusCode != HttpStatus.SC_OK) {
-               String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'.  Server returned status code %d",
-                               specifier.getURNValue(), client.getBaseURL(), statusCode);
-               throw new DocumentException(statusCode, errMsg);
-       }
-    }
-
-    /**
-     * Request an authority item list payload from the SAS server.  This is a non-paging solution.  If the authority
-     * has a very large number of items/terms, we might not be able to handle them all.
-     *
-     * @param ctx
-     * @param specifier
-     * @return
-     * @throws Exception
-     */
-    private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
-       PoxPayloadIn result = null;
-        AuthorityClient client = (AuthorityClient) ctx.getClient();
-
-       //
-       // First find out how many items exist
-        Response res = client.readItemList(specifier.getURNValue(),
-                       null,   // partial term string
-                       null,   // keyword string
-                       0,              // page size
-                       0               // page number
-                       );
-        assertStatusCode(res, specifier, client);
-        AbstractCommonList commonList;
-        try {
-               commonList = res.readEntity(AbstractCommonList.class);
-        } finally {
-               res.close();
-        }
-        long numOfItems = commonList.getTotalItems();
-
-        //
-        // Next, request a payload list with all the items
-        res = client.readItemList(specifier.getURNValue(),
-                       null,           // partial term string
-                       null,           // keyword string
-                       numOfItems,     // page size
-                       0                       // page number
-                       );
-        assertStatusCode(res, specifier, client);
-        try {
-            result = new PoxPayloadIn((String)res.readEntity(getEntityResponseType())); // Get the entire response.
-        } finally {
-               res.close();
-        }
-
-       return result;
-    }
-
-
-    /*
-     * Non standard injection of CSID into common part, since caller may access through
-     * shortId, and not know the CSID.
-     * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
-     */
-    @Override
-    protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
-            throws Exception {
-        Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
-
-        // Add the CSID to the common part
-        if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
-            String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
-            unQObjectProperties.put("csid", csid);
-        }
-
-        return unQObjectProperties;
-    }
-
-    public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
-       super.fillAllParts(wrapDoc, action);
-       //
-       // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
-       //
-       if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
-               updateRevNumbers(wrapDoc);
-       }
-    }
-
-    protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
-       DocumentModel documentModel = wrapDoc.getWrappedObject();
-       Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
-       if (rev == null) {
-               rev = (long)0;
-       } else {
-               rev++;
-       }
-       documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
-    }
-
-    /*
-     * We consider workflow state changes as changes that should bump the revision number
-     * (non-Javadoc)
-     * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
-     */
-    @Override
-    public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
-       boolean updateRevNumber = this.getShouldUpdateRevNumber();
-        Boolean contextProperty = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
-        if (contextProperty != null) {
-               updateRevNumber = contextProperty;
-        }
-
-       if (updateRevNumber == true) { // We don't update the rev number of synchronization requests
-               updateRevNumbers(wrapDoc);
-       }
-    }
-
-    @Override
-    public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
-        super.handleCreate(wrapDoc);
-        // CSPACE-3178:
-        // Uncomment once debugged and App layer is read to integrate
-        // Experimenting with this uncommented now ...
-        handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
-        updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
-    }
-
-    protected String buildWhereForShortId(String name) {
-        return authorityCommonSchemaName
-                + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
-                + "='" + name + "'";
-    }
-
-    private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
-       return true;
-    }
-
-    private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
-       boolean result = true;
-
-       ServiceContext ctx = this.getServiceContext();
-        String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
-       String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
-       try {
+               } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
+                       result = -1;
+                       logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
+                                       itemIdentifier));
+               }
+
+               return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
+
+
+       }
+
+       private void assertStatusCode(Response res, Specifier specifier, AuthorityClient client) throws Exception {
+               int statusCode = res.getStatus();
+
+               if (statusCode != HttpStatus.SC_OK) {
+                       String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'.  Server returned status code %d",
+                                       specifier.getURNValue(), client.getBaseURL(), statusCode);
+                       throw new DocumentException(statusCode, errMsg);
+               }
+       }
+
+       /**
+        * Request an authority item list payload from the SAS server.  This is a non-paging solution.  If the authority
+        * has a very large number of items/terms, we might not be able to handle them all.
+        *
+        * @param ctx
+        * @param specifier
+        * @return
+        * @throws Exception
+        */
+       private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
+               PoxPayloadIn result = null;
+               AuthorityClient client = (AuthorityClient) ctx.getClient();
+
+               //
+               // First find out how many items exist
+               Response res = client.readItemList(specifier.getURNValue(),
+                               null,   // partial term string
+                               null,   // keyword string
+                               0,              // page size
+                               0               // page number
+                               );
+               assertStatusCode(res, specifier, client);
+               AbstractCommonList commonList;
+               try {
+                       commonList = res.readEntity(AbstractCommonList.class);
+               } finally {
+                       res.close();
+               }
+               long numOfItems = commonList.getTotalItems();
+
+               //
+               // Next, request a payload list with all the items
+               res = client.readItemList(specifier.getURNValue(),
+                               null,           // partial term string
+                               null,           // keyword string
+                               numOfItems,     // page size
+                               0                       // page number
+                               );
+               assertStatusCode(res, specifier, client);
+               try {
+                       result = new PoxPayloadIn((String)res.readEntity(getEntityResponseType())); // Get the entire response.
+               } finally {
+                       res.close();
+               }
+
+               return result;
+       }
+
+
+       /*
+        * Non standard injection of CSID into common part, since caller may access through
+        * shortId, and not know the CSID.
+        * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
+        */
+       @Override
+       protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
+                       throws Exception {
+               Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
+
+               // Add the CSID to the common part
+               if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
+                       String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
+                       unQObjectProperties.put("csid", csid);
+               }
+
+               return unQObjectProperties;
+       }
+
+       public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
+               super.fillAllParts(wrapDoc, action);
+               //
+               // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
+               //
+               if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
+                       updateRevNumbers(wrapDoc);
+               }
+       }
+
+       protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
+               DocumentModel documentModel = wrapDoc.getWrappedObject();
+               Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
+               if (rev == null) {
+                       rev = (long)0;
+               } else {
+                       rev++;
+               }
+               documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
+       }
+
+       /*
+        * We consider workflow state changes as changes that should bump the revision number
+        * (non-Javadoc)
+        * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
+        */
+       @Override
+       public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
+               boolean updateRevNumber = this.getShouldUpdateRevNumber();
+               Boolean contextProperty = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
+               if (contextProperty != null) {
+                       updateRevNumber = contextProperty;
+               }
+
+               if (updateRevNumber == true) { // We don't update the rev number of synchronization requests
+                       updateRevNumbers(wrapDoc);
+               }
+       }
+
+       @Override
+       public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+               super.handleCreate(wrapDoc);
+               // CSPACE-3178:
+               // Uncomment once debugged and App layer is read to integrate
+               // Experimenting with this uncommented now ...
+               handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
+               updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
+       }
+
+       protected String buildWhereForShortId(String name) {
+               return authorityCommonSchemaName
+                               + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
+                               + "='" + name + "'";
+       }
+
+       private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
+               return true;
+       }
+
+       private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
+               boolean result = true;
+
+               ServiceContext ctx = this.getServiceContext();
+               String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
+               String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
+               try {
                        DocumentWrapper<DocumentModel> searchResultWrapper = getRepositoryClient(ctx).findDoc(ctx, nxqlWhereClause);
                        if (searchResultWrapper != null) {
                                result = false;
@@ -654,134 +654,134 @@ public abstract class AuthorityDocumentModelHandler<AuthCommon>
                        // Not a problem, just means we couldn't find another authority with that short ID
                }
 
-       return result;
-    }
-
-    /**
-     * If no short identifier was provided in the input payload,
-     * generate a short identifier from the display name. Either way though,
-     * the short identifier needs to be unique.
-     */
-    private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
-        String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
-        String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
-        String shortDisplayName = "";
-        String generateShortIdentifier = null;
-        if (Tools.isEmpty(shortIdentifier)) {
-               generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
-            docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
-        }
-
-        if (isUnique(docModel, schemaName) == false) {
-               String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
-               String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
-               String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
-                               errMsgVerb, shortId);
-               throw new DocumentException(errMsg);
-        }
-    }
-
-    /**
-     * Generate a refName for the authority from the short identifier
-     * and display name.
-     *
-     * All refNames for authorities are generated.  If a client supplies
-     * a refName, it will be overwritten during create (per this method)
-     * or discarded during update (per filterReadOnlyPropertiesForPart).
-     *
-     * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
-     *
-     */
-    protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
-        DocumentModel docModel = wrapDoc.getWrappedObject();
-        RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
-        String refName = authority.toString();
-        docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
-    }
-
-    @Override
-    public RefName.RefNameInterface getRefName(ServiceContext ctx,
-               DocumentModel docModel) {
-       RefName.RefNameInterface refname = null;
-
-       try {
-               String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
-               String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
-               RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
-                       ctx.getServiceName(),
-                       null,   // Only use shortId form!!!
-                       shortIdentifier,
-                       displayName);
-               refname = authority;
-       } catch (Exception e) {
-               logger.error(e.getMessage(), e);
-       }
-
-       return refname;
-    }
-
-    @Override
-    protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
-       String result = null;
-
-       DocumentModel docModel = docWrapper.getWrappedObject();
-       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
-       RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
-       result = refname.getDisplayName();
-
-       return result;
-    }
-
-    public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
-        String shortIdentifier = null;
-        CoreSessionInterface repoSession = null;
-        boolean releaseSession = false;
-
-       NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl)this.getRepositoryClient(ctx);
-        try {
-               repoSession = nuxeoRepoClient.getRepositorySession(ctx);
-            DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
-            DocumentModel docModel = wrapDoc.getWrappedObject();
-            if (docModel == null) {
-               throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
-            }
-            shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
-        } catch (ClientException ce) {
-            throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
-        } finally {
-               if (repoSession != null) {
-                       nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
-               }
-        }
-
-        return shortIdentifier;
-    }
-
-    /**
-     * Filters out selected values supplied in an update request.
-     *
-     * @param objectProps the properties filtered out from the update payload
-     * @param partMeta metadata for the object to fill
-     */
-    @Override
-    public void filterReadOnlyPropertiesForPart(
-            Map<String, Object> objectProps, ObjectPartType partMeta) {
-        super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
-        String commonPartLabel = getServiceContext().getCommonPartLabel();
-        if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
-            objectProps.remove(AuthorityJAXBSchema.CSID);
-            objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
-            objectProps.remove(AuthorityJAXBSchema.REF_NAME);
-        }
-    }
-
-    @Override
-    protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
-            String schema, ListResultField field) throws DocumentException {
-        Object result = null;
-
-        result = super.getListResultValue(docModel, schema, field);
-
-        return result;
-    }
+               return result;
+       }
+
+       /**
+        * If no short identifier was provided in the input payload,
+        * generate a short identifier from the display name. Either way though,
+        * the short identifier needs to be unique.
+        */
+       private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
+               String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
+               String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
+               String shortDisplayName = "";
+               String generateShortIdentifier = null;
+               if (Tools.isEmpty(shortIdentifier)) {
+                       generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
+                       docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
+               }
+
+               if (isUnique(docModel, schemaName) == false) {
+                       String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
+                       String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
+                       String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
+                                       errMsgVerb, shortId);
+                       throw new DocumentException(errMsg);
+               }
+       }
+
+       /**
+        * Generate a refName for the authority from the short identifier
+        * and display name.
+        *
+        * All refNames for authorities are generated.  If a client supplies
+        * a refName, it will be overwritten during create (per this method)
+        * or discarded during update (per filterReadOnlyPropertiesForPart).
+        *
+        * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
+        *
+        */
+       protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
+               DocumentModel docModel = wrapDoc.getWrappedObject();
+               RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
+               String refName = authority.toString();
+               docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
+       }
+
+       @Override
+       public RefName.RefNameInterface getRefName(ServiceContext ctx,
+                       DocumentModel docModel) {
+               RefName.RefNameInterface refname = null;
+
+               try {
+                       String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
+                       String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
+                       RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
+                                       ctx.getServiceName(),
+                                       null,   // Only use shortId form!!!
+                                       shortIdentifier,
+                                       displayName);
+                       refname = authority;
+               } catch (Exception e) {
+                       logger.error(e.getMessage(), e);
+               }
+
+               return refname;
+       }
+
+       @Override
+       protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
+               String result = null;
+
+               DocumentModel docModel = docWrapper.getWrappedObject();
+               ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
+               RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
+               result = refname.getDisplayName();
+
+               return result;
+       }
+
+       public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
+               String shortIdentifier = null;
+               CoreSessionInterface repoSession = null;
+               boolean releaseSession = false;
+
+               NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl)this.getRepositoryClient(ctx);
+               try {
+                       repoSession = nuxeoRepoClient.getRepositorySession(ctx);
+                       DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
+                       DocumentModel docModel = wrapDoc.getWrappedObject();
+                       if (docModel == null) {
+                               throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
+                       }
+                       shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
+               } catch (ClientException ce) {
+                       throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
+               } finally {
+                       if (repoSession != null) {
+                               nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
+                       }
+               }
+
+               return shortIdentifier;
+       }
+
+       /**
+        * Filters out selected values supplied in an update request.
+        *
+        * @param objectProps the properties filtered out from the update payload
+        * @param partMeta metadata for the object to fill
+        */
+       @Override
+       public void filterReadOnlyPropertiesForPart(
+                       Map<String, Object> objectProps, ObjectPartType partMeta) {
+               super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
+               String commonPartLabel = getServiceContext().getCommonPartLabel();
+               if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
+                       objectProps.remove(AuthorityJAXBSchema.CSID);
+                       objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
+                       objectProps.remove(AuthorityJAXBSchema.REF_NAME);
+               }
+       }
+
+       @Override
+       protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
+                       String schema, ListResultField field) throws DocumentException {
+               Object result = null;
+
+               result = super.getListResultValue(docModel, schema, field);
+
+               return result;
+       }
 }
index 688d0706c9eacb61e287e4af319203ad2bfc2f66..9d840f140be7d5e3a5ff897ef33adc0701360026 100644 (file)
@@ -84,1519 +84,1519 @@ import java.util.regex.PatternSyntaxException;
  * $LastChangedDate: $
  */
 public abstract class AuthorityItemDocumentModelHandler<AICommon>
-        extends NuxeoDocumentModelHandler<AICommon> {
-
-    private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
-    
-    private static final Integer PAGE_SIZE_FROM_QUERYPARAMS = null;
-    private static final Integer PAGE_NUM_FROM_QUERYPARAMS = null;
-    
-    protected String authorityCommonSchemaName;
-    protected String authorityItemCommonSchemaName;
-    private String authorityItemTermGroupXPathBase;
-    
-    private boolean shouldUpdateSASFields = true;
-    private boolean syncHierarchicalRelationships = false;
-    private boolean isProposed = false; // used by local authority to propose a new shared item. Allows local deployments to use new terms until they become official
-    private boolean isSAS = false; // used to indicate if the authority item originated as a SAS item
-    private boolean shouldUpdateRevNumber = true; // by default we should update the revision number -not true on synchronization with SAS
-    /**
-     * inVocabulary is the parent Authority for this context
-     */
-    protected String inAuthority = null;
-    protected boolean wildcardedAuthorityRequest = false;
-    protected String authorityRefNameBase = null;
-    // Used to determine when the displayName changes as part of the update.
-    protected String oldDisplayNameOnUpdate = null;
-    private final static String LIST_SUFFIX = "List";
-    private final static String ZERO_OR_MORE_ANY_CHAR_REGEX = ".*";
-
-    public AuthorityItemDocumentModelHandler(String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
-        this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
-    }
-    
-    abstract public String getParentCommonSchemaName();
-    
-    //
-    // Getter and Setter for 'shouldUpdateSASFields'
-    //
-    public boolean getShouldUpdateSASFields() {
-        return shouldUpdateSASFields;
-    }
-    
-    public void setshouldUpdateSASFields(boolean flag) {
-        shouldUpdateSASFields = flag;
-    }
-    
-    //
-    // Getter and Setter for 'proposed'
-    //
-    public boolean getIsProposed() {
-        return this.isProposed;
-    }
-    
-    public void setIsProposed(boolean flag) {
-        this.isProposed = flag;
-    }
-    
-    //
-    // Getter and Setter for 'isSAS'
-    //
-    public boolean getIsSASItem() {
-        return this.isSAS;
-    }
-
-    public void setIsSASItem(boolean flag) {
-        this.isSAS = flag;
-    }
-    
-    //
-    // Getter and Setter for 'shouldUpdateRevNumber'
-    //
-    public boolean getShouldUpdateRevNumber() {
-        return this.shouldUpdateRevNumber;
-    }
-    
-    public void setShouldUpdateRevNumber(boolean flag) {
-        this.shouldUpdateRevNumber = flag;
-    }
-
-    //
-    // Getter and Setter for deciding if we need to synch hierarchical relationships
-    //
-    public boolean getShouldSyncHierarchicalRelationships() {
-        return this.syncHierarchicalRelationships;
-    }
-    
-    public void setShouldSyncHierarchicalRelationships(boolean flag) {
-        this.syncHierarchicalRelationships = flag;
-    }
-
-    @Override
-    public void prepareSync() throws Exception {
-        this.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV);  // Never update rev nums on sync operations
-    }
-
-    @Override
-    protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
-        String result = null;
-        
-        DocumentModel docModel = docWrapper.getWrappedObject();
-        ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
-        RefName.AuthorityItem refname = (RefName.AuthorityItem)getRefName(ctx, docModel);
-        result = refname.getDisplayName();
-        
-        return result;
-    }
-    
-    /*
-     * After calling this method successfully, the document model will contain an updated refname and short ID
-     * (non-Javadoc)
-     * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#getRefName(org.collectionspace.services.common.context.ServiceContext, org.nuxeo.ecm.core.api.DocumentModel)
-     */
-    @Override
-    public RefName.RefNameInterface getRefName(ServiceContext ctx,
-            DocumentModel docModel) {
-        RefName.RefNameInterface refname = null;
-        
-        try {
-            String displayName = getPrimaryDisplayName(docModel, authorityItemCommonSchemaName,
-                    getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
-            if (Tools.isEmpty(displayName)) {
-                throw new Exception("The displayName for this authority term was empty or not set.");
-            }
-        
-            String shortIdentifier = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
-            if (Tools.isEmpty(shortIdentifier)) {
-                // We didn't find a short ID in the payload request, so we need to synthesize one.
-                shortIdentifier = handleDisplayNameAsShortIdentifier(docModel); // updates the document model with the new short ID as a side-effect
-            }
-            
-            String authorityRefBaseName = getAuthorityRefNameBase();
-            if (Tools.isEmpty(authorityRefBaseName)) {
-                throw new Exception("Could not create the refName for this authority term, because the refName for its authority parent was empty.");
-            }
-            
-            // Create the items refname using the parent's as a base
-            RefName.Authority parentsRefName = RefName.Authority.parse(authorityRefBaseName);
-            refname = RefName.buildAuthorityItem(parentsRefName, shortIdentifier, displayName);
-            // Now update the document model with the refname value
-            String refNameStr = refname.toString();
-            docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REF_NAME, refNameStr); // REM - This field is deprecated now that the refName is part of the collection_space core schema
-
-        } catch (Exception e) {
-            logger.error(e.getMessage(), e);
-        }
-
-        return refname;
-    }
-    
-    public void setInAuthority(String inAuthority) {
-        this.inAuthority = inAuthority;
-    }
-    
+               extends NuxeoDocumentModelHandler<AICommon> {
+
+       private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
+
+       private static final Integer PAGE_SIZE_FROM_QUERYPARAMS = null;
+       private static final Integer PAGE_NUM_FROM_QUERYPARAMS = null;
+
+       protected String authorityCommonSchemaName;
+       protected String authorityItemCommonSchemaName;
+       private String authorityItemTermGroupXPathBase;
+
+       private boolean shouldUpdateSASFields = true;
+       private boolean syncHierarchicalRelationships = false;
+       private boolean isProposed = false; // used by local authority to propose a new shared item. Allows local deployments to use new terms until they become official
+       private boolean isSAS = false; // used to indicate if the authority item originated as a SAS item
+       private boolean shouldUpdateRevNumber = true; // by default we should update the revision number -not true on synchronization with SAS
+       /**
+        * inVocabulary is the parent Authority for this context
+        */
+       protected String inAuthority = null;
+       protected boolean wildcardedAuthorityRequest = false;
+       protected String authorityRefNameBase = null;
+       // Used to determine when the displayName changes as part of the update.
+       protected String oldDisplayNameOnUpdate = null;
+       private final static String LIST_SUFFIX = "List";
+       private final static String ZERO_OR_MORE_ANY_CHAR_REGEX = ".*";
+
+       public AuthorityItemDocumentModelHandler(String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
+               this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
+       }
+
+       abstract public String getParentCommonSchemaName();
+
+       //
+       // Getter and Setter for 'shouldUpdateSASFields'
+       //
+       public boolean getShouldUpdateSASFields() {
+               return shouldUpdateSASFields;
+       }
+
+       public void setshouldUpdateSASFields(boolean flag) {
+               shouldUpdateSASFields = flag;
+       }
+
+       //
+       // Getter and Setter for 'proposed'
+       //
+       public boolean getIsProposed() {
+               return this.isProposed;
+       }
+
+       public void setIsProposed(boolean flag) {
+               this.isProposed = flag;
+       }
+
+       //
+       // Getter and Setter for 'isSAS'
+       //
+       public boolean getIsSASItem() {
+               return this.isSAS;
+       }
+
+       public void setIsSASItem(boolean flag) {
+               this.isSAS = flag;
+       }
+
+       //
+       // Getter and Setter for 'shouldUpdateRevNumber'
+       //
+       public boolean getShouldUpdateRevNumber() {
+               return this.shouldUpdateRevNumber;
+       }
+
+       public void setShouldUpdateRevNumber(boolean flag) {
+               this.shouldUpdateRevNumber = flag;
+       }
+
+       //
+       // Getter and Setter for deciding if we need to synch hierarchical relationships
+       //
+       public boolean getShouldSyncHierarchicalRelationships() {
+               return this.syncHierarchicalRelationships;
+       }
+
+       public void setShouldSyncHierarchicalRelationships(boolean flag) {
+               this.syncHierarchicalRelationships = flag;
+       }
+
+       @Override
+       public void prepareSync() throws Exception {
+               this.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV);  // Never update rev nums on sync operations
+       }
+
+       @Override
+       protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
+               String result = null;
+
+               DocumentModel docModel = docWrapper.getWrappedObject();
+               ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
+               RefName.AuthorityItem refname = (RefName.AuthorityItem)getRefName(ctx, docModel);
+               result = refname.getDisplayName();
+
+               return result;
+       }
+
+       /*
+        * After calling this method successfully, the document model will contain an updated refname and short ID
+        * (non-Javadoc)
+        * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#getRefName(org.collectionspace.services.common.context.ServiceContext, org.nuxeo.ecm.core.api.DocumentModel)
+        */
+       @Override
+       public RefName.RefNameInterface getRefName(ServiceContext ctx,
+                       DocumentModel docModel) {
+               RefName.RefNameInterface refname = null;
+
+               try {
+                       String displayName = getPrimaryDisplayName(docModel, authorityItemCommonSchemaName,
+                                       getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
+                       if (Tools.isEmpty(displayName)) {
+                               throw new Exception("The displayName for this authority term was empty or not set.");
+                       }
+
+                       String shortIdentifier = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
+                       if (Tools.isEmpty(shortIdentifier)) {
+                               // We didn't find a short ID in the payload request, so we need to synthesize one.
+                               shortIdentifier = handleDisplayNameAsShortIdentifier(docModel); // updates the document model with the new short ID as a side-effect
+                       }
+
+                       String authorityRefBaseName = getAuthorityRefNameBase();
+                       if (Tools.isEmpty(authorityRefBaseName)) {
+                               throw new Exception("Could not create the refName for this authority term, because the refName for its authority parent was empty.");
+                       }
+
+                       // Create the items refname using the parent's as a base
+                       RefName.Authority parentsRefName = RefName.Authority.parse(authorityRefBaseName);
+                       refname = RefName.buildAuthorityItem(parentsRefName, shortIdentifier, displayName);
+                       // Now update the document model with the refname value
+                       String refNameStr = refname.toString();
+                       docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REF_NAME, refNameStr); // REM - This field is deprecated now that the refName is part of the collection_space core schema
+
+               } catch (Exception e) {
+                       logger.error(e.getMessage(), e);
+               }
+
+               return refname;
+       }
+
+       public void setInAuthority(String inAuthority) {
+               this.inAuthority = inAuthority;
+       }
+
    public String getInAuthorityCsid() {
-        return this.inAuthority;
-    }
-
-    /** Subclasses may override this to customize the URI segment. */
-    public String getAuthorityServicePath() {
-        return getServiceContext().getServiceName().toLowerCase();    // Laramie20110510 CSPACE-3932
-    }
-
-    @Override
-    public String getUri(DocumentModel docModel) {
-        // Laramie20110510 CSPACE-3932
-        String authorityServicePath = getAuthorityServicePath();
-        if(inAuthority==null) {    // Only true with the first document model received, on queries to wildcarded authorities
-            wildcardedAuthorityRequest = true;
-        }
-        // If this search crosses multiple authorities, get the inAuthority value
-        // from each record, rather than using the cached value from the first record
-        if(wildcardedAuthorityRequest) {
-            try {
-                inAuthority = (String) docModel.getProperty(authorityItemCommonSchemaName,
-                    AuthorityItemJAXBSchema.IN_AUTHORITY);
-            } catch (ClientException pe) {
-                throw new RuntimeException("Could not get parent specifier for item!");
-            }
-        }
-        return "/" + authorityServicePath + '/' + inAuthority + '/' + AuthorityClient.ITEMS + '/' + getCsid(docModel);
-    }
-
-    protected String getAuthorityRefNameBase() {
-        return this.authorityRefNameBase;
-    }
-
-    public void setAuthorityRefNameBase(String value) {
-        this.authorityRefNameBase = value;
-    }
-
-    /*
-     * Note: the Vocabulary service's VocabularyItemDocumentModelHandler class overrides this method.
-     */
-    protected ListResultField getListResultsDisplayNameField() {
-        ListResultField result = new ListResultField();
-        // Per CSPACE-5132, the name of this element remains 'displayName'
-        // for backwards compatibility, although its value is obtained
-        // from the termDisplayName field.
-        //
-        // Update: this name is now being changed to 'termDisplayName', both
-        // because this is the actual field name and because the app layer
-        // work to convert over to this field is underway. Per Patrick, the
-        // app layer treats lists, in at least some context(s), as sparse record
-        // payloads, and thus fields in list results must all be present in
-        // (i.e. represent a strict subset of the fields in) record schemas.
-        // - ADR 2012-05-11
-        // 
-        //
-        // In CSPACE-5134, these list results will change substantially
-        // to return display names for both the preferred term and for
-        // each non-preferred term (if any).
-        result.setElement(AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
-        result.setXpath(NuxeoUtils.getPrimaryXPathPropertyName(
-                authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_DISPLAY_NAME));
-        
-        return result;
-    }
-    
-    /*
-     * Note: the Vocabulary service's VocabularyItemDocumentModelHandler class overrides this method.
-     */    
-    protected ListResultField getListResultsTermStatusField() {
-        ListResultField result = new ListResultField();
-        
-        result.setElement(AuthorityItemJAXBSchema.TERM_STATUS);
-        result.setXpath(NuxeoUtils.getPrimaryXPathPropertyName(
-                authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_STATUS));
-
-        return result;
-    }    
-    
-    private boolean isTermDisplayName(String elName) {
-        return AuthorityItemJAXBSchema.TERM_DISPLAY_NAME.equals(elName) || VocabularyItemJAXBSchema.DISPLAY_NAME.equals(elName);
-    }
-    
-    /*
-     * (non-Javadoc)
-     * @see org.collectionspace.services.nuxeo.client.java.DocHandlerBase#getListItemsArray()
-     * 
-     * Note: We're updating the "global" service and tenant bindings instance here -the list instance here is
-     * a reference to the tenant bindings instance in the singleton ServiceMain.
-     */
-    @Override
-    public List<ListResultField> getListItemsArray() throws DocumentException {
-        List<ListResultField> list = super.getListItemsArray();
-        
-        // One-time initialization for each authority item service.
-        if (isListItemArrayExtended() == false) {
-            synchronized(AuthorityItemDocumentModelHandler.class) {
-                if (isListItemArrayExtended() == false) {                    
-                    int nFields = list.size();
-                    // Ensure that each item in a list of Authority items includes
-                    // a set of common fields, so we do not depend upon configuration
-                    // for general logic.
-                     List<Integer> termDisplayNamePositionsInList = new ArrayList<>();
-                           boolean hasShortId = false;
-                    boolean hasTermStatus = false;
-                    for (int i = 0; i < nFields; i++) {
-                        ListResultField field = list.get(i);
-                        String elName = field.getElement();
-                        if (isTermDisplayName(elName) == true) {
-                            termDisplayNamePositionsInList.add(i);
-                        } else if (AuthorityItemJAXBSchema.SHORT_IDENTIFIER.equals(elName)) {
-                            hasShortId = true;
-                        } else if (AuthorityItemJAXBSchema.TERM_STATUS.equals(elName)) {
-                            hasTermStatus = true;
-                        }
-                    }
-                    
-                    ListResultField field;
-                        
-                        // Certain fields in authority item list results
-                        // are handled specially here
-                        
-                        // Term display name
-                        //
-                        // Ignore (throw out) any configuration entries that
-                        // specify how the termDisplayName field should be
-                        // emitted in authority item lists. This field will
-                        // be handled in a standardized manner (see block below).
-                        if (termDisplayNamePositionsInList.isEmpty() == false) {
-                            // Remove matching items starting at the end of the list
-                            // and moving towards the start, so that reshuffling of
-                            // list order doesn't alter the positions of earlier items
-                            Collections.sort(termDisplayNamePositionsInList, Collections.reverseOrder());
-                            for (int i : termDisplayNamePositionsInList) {
-                                list.remove(i);
-                            }
-                        }
-                        // termDisplayName values in authority item lists
-                        // will be handled via code that emits display names
-                        // for both the preferred term and all non-preferred
-                        // terms (if any). The following is a placeholder
-                        // entry that will trigger this code. See the
-                        // getListResultValue() method in this class.
-                    field = getListResultsDisplayNameField();
-                    list.add(field);
-                        
-                        // Short identifier
-                    if (!hasShortId) {
-                        field = new ListResultField();
-                        field.setElement(AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
-                        field.setXpath(AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
-                        list.add(field);
-                    }
-                        
-                        // Term status
-                    if (!hasTermStatus) {
-                        field = getListResultsTermStatusField();
-                        list.add(field);
-                    }
-                        
-                }
-     
-                setListItemArrayExtended(true);
-            } // end of synchronized block
-        }
-
-        return list;
-    }
-    
-    /**
-     * We consider workflow state changes as a change that should bump the revision number.
-     * Warning: This method might change the transitionDef's transtionName value
-     */
-    @Override
-    public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
-        // Decide whether or not to update the revision number
-        if (this.getShouldUpdateRevNumber() == true) { // We don't update the rev number of synchronization requests
-            updateRevNumbers(wrapDoc);
-        }
-        //
-        // We can't delete an authority item that has referencing records.
-        //
-        DocumentModel docModel = wrapDoc.getWrappedObject();
-        if (transitionDef.getName().equalsIgnoreCase(WorkflowClient.WORKFLOWTRANSITION_DELETE)) {
-            AuthorityRefDocList refsToAllObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.ALL);
-            AuthorityRefDocList refsToSoftDeletedObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.DELETED_ONLY);
-            if (refsToAllObjects.getTotalItems() > 0) {
-                if (refsToAllObjects.getTotalItems() > refsToSoftDeletedObjects.getTotalItems()) {
-                    //
-                    // If the number of refs to active objects is greater than the number of refs to
-                    // soft deleted objects then we can't delete the item.
-                    //
-                    logger.error(String.format("Cannot delete authority item CSID='%s' because it still has records in the system that are referencing it.",
-                            docModel.getName()));
-                    if (logger.isWarnEnabled() == true) {
-                        logReferencingObjects(docModel, refsToAllObjects);
-                    }
-
-                    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()));
-                }
-            }
-        }
-    }
-    
-    /**
-     * 
-     * @param wrapDoc
-     * @return
-     * @throws Exception
-     */
-    protected boolean handleRelationsSync(DocumentWrapper<Object> wrapDoc) throws Exception {
-        boolean result = false;
-        ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
-
-        //
-        // Get information about the local authority item so we can compare with corresponding item on the shared authority server
-        //
-        AuthorityItemSpecifier authorityItemSpecifier = (AuthorityItemSpecifier) wrapDoc.getWrappedObject();
-        DocumentModel itemDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), getAuthorityItemCommonSchemaName(), 
-                authorityItemSpecifier);
-        if (itemDocModel == null) {
-            throw new DocumentNotFoundException(String.format("Could not find authority item resource with CSID='%s'",
-                    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 = itemDocModel.getCurrentLifeCycleState();
-        String itemShortId = (String) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
-        
-        //
-        // Now get the item's Authority (the parent) information
-        //
-        DocumentModel authorityDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName,
-                authorityItemSpecifier.getParentSpecifier());
-        String authorityShortId = (String)NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
-        String localParentCsid = authorityDocModel.getName();
-        String remoteClientConfigName = (String)NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME);
-        //
-        // Using the short IDs of the local authority and item, create URN specifiers and retrieve the SAS authority item
-        //
-        AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authorityShortId, itemShortId);
-        // Get the shared authority server's copy
-        PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier, 
-                remoteClientConfigName, getAuthorityServicePath(), getEntityResponseType(), AuthorityClient.INCLUDE_RELATIONS);
-        
-        //
-        // Get the RelationsCommonList and remove the CSIDs since they are for remote items only. We'll use
-        // the refnames in the payload instead to find the local CSIDs
-        //
-        PayloadInputPart relationsCommonListPart = sasPayloadIn.getPart(RelationClient.SERVICE_COMMON_LIST_NAME);
-        relationsCommonListPart.clearElementBody(); // clear the existing DOM element that was created from the incoming XML payload
-        RelationsCommonList rcl = (RelationsCommonList) relationsCommonListPart.getBody();  // Get the JAX-B object and clear the CSID values
-        for (RelationsCommonList.RelationListItem listItem : rcl.getRelationListItem()) {
-            // clear the remote relation item's CSID
-            listItem.setCsid(null);
-            // clear the remote subject's CSID
-            listItem.setSubjectCsid(null);
-            listItem.getSubject().setCsid(null);
-            listItem.getSubject().setUri(null);
-            // clear the remote object's CSID
-            listItem.setObjectCsid(null);
-            listItem.getObject().setCsid(null);
-            listItem.getObject().setUri(null);
-        }
-        
-        //
-        // Remove all the payload parts except the relations part since we only want to sync the relationships
-        //
-        ArrayList<PayloadInputPart> newPartList = new ArrayList<PayloadInputPart>();
-        newPartList.add(relationsCommonListPart); // add our CSID filtered RelationsCommonList part
-        sasPayloadIn.setParts(newPartList);
-        sasPayloadIn = new PoxPayloadIn(sasPayloadIn.toXML()); // Builds a new payload using the current set of parts -i.e., just the relations part
-        
-        sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter the domain name part of any and all refnames in the payload
-        AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath());
-        PoxPayloadOut payloadOut = authorityResource.updateAuthorityItem(ctx, 
-                ctx.getResourceMap(),                     
-                ctx.getUriInfo(),
-                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.NOT_PROPOSED,        // The items is not proposed, make it a real SAS item now
-                AuthorityServiceUtils.SAS_ITEM);        // Since we're sync'ing, this must be a SAS item
-        if (payloadOut != null) {    
-            ctx.setOutput(payloadOut);
-            result = true;
-        }        
-        
-        return result;
-    }
-        
-    @Override
-    public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
-        boolean result = false;
-
-        if (this.getShouldSyncHierarchicalRelationships() == true) {
-            result = handleRelationsSync(wrapDoc);
-        } else {
-            result = handlePayloadSync(wrapDoc);
-        }
-        
-        return result;
-    }
-    
-    /**
-     * 
-     * @param wrapDoc
-     * @return
-     * @throws Exception
-     */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
+               return this.inAuthority;
+       }
+
+       /** Subclasses may override this to customize the URI segment. */
+       public String getAuthorityServicePath() {
+               return getServiceContext().getServiceName().toLowerCase();      // Laramie20110510 CSPACE-3932
+       }
+
+       @Override
+       public String getUri(DocumentModel docModel) {
+               // Laramie20110510 CSPACE-3932
+               String authorityServicePath = getAuthorityServicePath();
+               if(inAuthority==null) { // Only true with the first document model received, on queries to wildcarded authorities
+                       wildcardedAuthorityRequest = true;
+               }
+               // If this search crosses multiple authorities, get the inAuthority value
+               // from each record, rather than using the cached value from the first record
+               if(wildcardedAuthorityRequest) {
+                       try {
+                               inAuthority = (String) docModel.getProperty(authorityItemCommonSchemaName,
+                                       AuthorityItemJAXBSchema.IN_AUTHORITY);
+                       } catch (ClientException pe) {
+                               throw new RuntimeException("Could not get parent specifier for item!");
+                       }
+               }
+               return "/" + authorityServicePath + '/' + inAuthority + '/' + AuthorityClient.ITEMS + '/' + getCsid(docModel);
+       }
+
+       protected String getAuthorityRefNameBase() {
+               return this.authorityRefNameBase;
+       }
+
+       public void setAuthorityRefNameBase(String value) {
+               this.authorityRefNameBase = value;
+       }
+
+       /*
+        * Note: the Vocabulary service's VocabularyItemDocumentModelHandler class overrides this method.
+        */
+       protected ListResultField getListResultsDisplayNameField() {
+               ListResultField result = new ListResultField();
+               // Per CSPACE-5132, the name of this element remains 'displayName'
+               // for backwards compatibility, although its value is obtained
+               // from the termDisplayName field.
+               //
+               // Update: this name is now being changed to 'termDisplayName', both
+               // because this is the actual field name and because the app layer
+               // work to convert over to this field is underway. Per Patrick, the
+               // app layer treats lists, in at least some context(s), as sparse record
+               // payloads, and thus fields in list results must all be present in
+               // (i.e. represent a strict subset of the fields in) record schemas.
+               // - ADR 2012-05-11
+               //
+               //
+               // In CSPACE-5134, these list results will change substantially
+               // to return display names for both the preferred term and for
+               // each non-preferred term (if any).
+               result.setElement(AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
+               result.setXpath(NuxeoUtils.getPrimaryXPathPropertyName(
+                               authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_DISPLAY_NAME));
+
+               return result;
+       }
+
+       /*
+        * Note: the Vocabulary service's VocabularyItemDocumentModelHandler class overrides this method.
+        */
+       protected ListResultField getListResultsTermStatusField() {
+               ListResultField result = new ListResultField();
+
+               result.setElement(AuthorityItemJAXBSchema.TERM_STATUS);
+               result.setXpath(NuxeoUtils.getPrimaryXPathPropertyName(
+                               authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(), AuthorityItemJAXBSchema.TERM_STATUS));
+
+               return result;
+       }
+
+       private boolean isTermDisplayName(String elName) {
+               return AuthorityItemJAXBSchema.TERM_DISPLAY_NAME.equals(elName) || VocabularyItemJAXBSchema.DISPLAY_NAME.equals(elName);
+       }
+
+       /*
+        * (non-Javadoc)
+        * @see org.collectionspace.services.nuxeo.client.java.DocHandlerBase#getListItemsArray()
+        *
+        * Note: We're updating the "global" service and tenant bindings instance here -the list instance here is
+        * a reference to the tenant bindings instance in the singleton ServiceMain.
+        */
+       @Override
+       public List<ListResultField> getListItemsArray() throws DocumentException {
+               List<ListResultField> list = super.getListItemsArray();
+
+               // One-time initialization for each authority item service.
+               if (isListItemArrayExtended() == false) {
+                       synchronized(AuthorityItemDocumentModelHandler.class) {
+                               if (isListItemArrayExtended() == false) {
+                                       int nFields = list.size();
+                                       // Ensure that each item in a list of Authority items includes
+                                       // a set of common fields, so we do not depend upon configuration
+                                       // for general logic.
+                                        List<Integer> termDisplayNamePositionsInList = new ArrayList<>();
+                                                  boolean hasShortId = false;
+                                       boolean hasTermStatus = false;
+                                       for (int i = 0; i < nFields; i++) {
+                                               ListResultField field = list.get(i);
+                                               String elName = field.getElement();
+                                               if (isTermDisplayName(elName) == true) {
+                                                       termDisplayNamePositionsInList.add(i);
+                                               } else if (AuthorityItemJAXBSchema.SHORT_IDENTIFIER.equals(elName)) {
+                                                       hasShortId = true;
+                                               } else if (AuthorityItemJAXBSchema.TERM_STATUS.equals(elName)) {
+                                                       hasTermStatus = true;
+                                               }
+                                       }
+
+                                       ListResultField field;
+
+                                               // Certain fields in authority item list results
+                                               // are handled specially here
+
+                                               // Term display name
+                                               //
+                                               // Ignore (throw out) any configuration entries that
+                                               // specify how the termDisplayName field should be
+                                               // emitted in authority item lists. This field will
+                                               // be handled in a standardized manner (see block below).
+                                               if (termDisplayNamePositionsInList.isEmpty() == false) {
+                                                       // Remove matching items starting at the end of the list
+                                                       // and moving towards the start, so that reshuffling of
+                                                       // list order doesn't alter the positions of earlier items
+                                                       Collections.sort(termDisplayNamePositionsInList, Collections.reverseOrder());
+                                                       for (int i : termDisplayNamePositionsInList) {
+                                                               list.remove(i);
+                                                       }
+                                               }
+                                               // termDisplayName values in authority item lists
+                                               // will be handled via code that emits display names
+                                               // for both the preferred term and all non-preferred
+                                               // terms (if any). The following is a placeholder
+                                               // entry that will trigger this code. See the
+                                               // getListResultValue() method in this class.
+                                       field = getListResultsDisplayNameField();
+                                       list.add(field);
+
+                                               // Short identifier
+                                       if (!hasShortId) {
+                                               field = new ListResultField();
+                                               field.setElement(AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
+                                               field.setXpath(AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
+                                               list.add(field);
+                                       }
+
+                                               // Term status
+                                       if (!hasTermStatus) {
+                                               field = getListResultsTermStatusField();
+                                               list.add(field);
+                                       }
+
+                               }
+
+                               setListItemArrayExtended(true);
+                       } // end of synchronized block
+               }
+
+               return list;
+       }
+
+       /**
+        * We consider workflow state changes as a change that should bump the revision number.
+        * Warning: This method might change the transitionDef's transtionName value
+        */
+       @Override
+       public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
+               // Decide whether or not to update the revision number
+               if (this.getShouldUpdateRevNumber() == true) { // We don't update the rev number of synchronization requests
+                       updateRevNumbers(wrapDoc);
+               }
+               //
+               // We can't delete an authority item that has referencing records.
+               //
+               DocumentModel docModel = wrapDoc.getWrappedObject();
+               if (transitionDef.getName().equalsIgnoreCase(WorkflowClient.WORKFLOWTRANSITION_DELETE)) {
+                       AuthorityRefDocList refsToAllObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.ALL);
+                       AuthorityRefDocList refsToSoftDeletedObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.DELETED_ONLY);
+                       if (refsToAllObjects.getTotalItems() > 0) {
+                               if (refsToAllObjects.getTotalItems() > refsToSoftDeletedObjects.getTotalItems()) {
+                                       //
+                                       // If the number of refs to active objects is greater than the number of refs to
+                                       // soft deleted objects then we can't delete the item.
+                                       //
+                                       logger.error(String.format("Cannot delete authority item CSID='%s' because it still has records in the system that are referencing it.",
+                                                       docModel.getName()));
+                                       if (logger.isWarnEnabled() == true) {
+                                               logReferencingObjects(docModel, refsToAllObjects);
+                                       }
+
+                                       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()));
+                               }
+                       }
+               }
+       }
+
+       /**
+        *
+        * @param wrapDoc
+        * @return
+        * @throws Exception
+        */
+       protected boolean handleRelationsSync(DocumentWrapper<Object> wrapDoc) throws Exception {
+               boolean result = false;
+               ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
+
+               //
+               // Get information about the local authority item so we can compare with corresponding item on the shared authority server
+               //
+               AuthorityItemSpecifier authorityItemSpecifier = (AuthorityItemSpecifier) wrapDoc.getWrappedObject();
+               DocumentModel itemDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), getAuthorityItemCommonSchemaName(),
+                               authorityItemSpecifier);
+               if (itemDocModel == null) {
+                       throw new DocumentNotFoundException(String.format("Could not find authority item resource with CSID='%s'",
+                                       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 = itemDocModel.getCurrentLifeCycleState();
+               String itemShortId = (String) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
+
+               //
+               // Now get the item's Authority (the parent) information
+               //
+               DocumentModel authorityDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName,
+                               authorityItemSpecifier.getParentSpecifier());
+               String authorityShortId = (String)NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
+               String localParentCsid = authorityDocModel.getName();
+               String remoteClientConfigName = (String)NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME);
+               //
+               // Using the short IDs of the local authority and item, create URN specifiers and retrieve the SAS authority item
+               //
+               AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authorityShortId, itemShortId);
+               // Get the shared authority server's copy
+               PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier,
+                               remoteClientConfigName, getAuthorityServicePath(), getEntityResponseType(), AuthorityClient.INCLUDE_RELATIONS);
+
+               //
+               // Get the RelationsCommonList and remove the CSIDs since they are for remote items only. We'll use
+               // the refnames in the payload instead to find the local CSIDs
+               //
+               PayloadInputPart relationsCommonListPart = sasPayloadIn.getPart(RelationClient.SERVICE_COMMON_LIST_NAME);
+               relationsCommonListPart.clearElementBody(); // clear the existing DOM element that was created from the incoming XML payload
+               RelationsCommonList rcl = (RelationsCommonList) relationsCommonListPart.getBody();  // Get the JAX-B object and clear the CSID values
+               for (RelationsCommonList.RelationListItem listItem : rcl.getRelationListItem()) {
+                       // clear the remote relation item's CSID
+                       listItem.setCsid(null);
+                       // clear the remote subject's CSID
+                       listItem.setSubjectCsid(null);
+                       listItem.getSubject().setCsid(null);
+                       listItem.getSubject().setUri(null);
+                       // clear the remote object's CSID
+                       listItem.setObjectCsid(null);
+                       listItem.getObject().setCsid(null);
+                       listItem.getObject().setUri(null);
+               }
+
+               //
+               // Remove all the payload parts except the relations part since we only want to sync the relationships
+               //
+               ArrayList<PayloadInputPart> newPartList = new ArrayList<PayloadInputPart>();
+               newPartList.add(relationsCommonListPart); // add our CSID filtered RelationsCommonList part
+               sasPayloadIn.setParts(newPartList);
+               sasPayloadIn = new PoxPayloadIn(sasPayloadIn.toXML()); // Builds a new payload using the current set of parts -i.e., just the relations part
+
+               sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter the domain name part of any and all refnames in the payload
+               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath());
+               PoxPayloadOut payloadOut = authorityResource.updateAuthorityItem(ctx,
+                               ctx.getResourceMap(),
+                               ctx.getUriInfo(),
+                               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.NOT_PROPOSED,             // The items is not proposed, make it a real SAS item now
+                               AuthorityServiceUtils.SAS_ITEM);                // Since we're sync'ing, this must be a SAS item
+               if (payloadOut != null) {
+                       ctx.setOutput(payloadOut);
+                       result = true;
+               }
+
+               return result;
+       }
+
+       @Override
+       public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
+               boolean result = false;
+
+               if (this.getShouldSyncHierarchicalRelationships() == true) {
+                       result = handleRelationsSync(wrapDoc);
+               } else {
+                       result = handlePayloadSync(wrapDoc);
+               }
+
+               return result;
+       }
+
+       /**
+        *
+        * @param wrapDoc
+        * @return
+        * @throws Exception
+        */
+       @SuppressWarnings({ "rawtypes", "unchecked" })
        protected boolean handlePayloadSync(DocumentWrapper<Object> wrapDoc) throws Exception {
-        boolean result = false;
-        ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
-        
-        //
-        // Get information about the local authority item so we can compare with corresponding item on the shared authority server
-        //
-        AuthorityItemSpecifier authorityItemSpecifier = (AuthorityItemSpecifier) wrapDoc.getWrappedObject();
-        DocumentModel itemDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), getAuthorityItemCommonSchemaName(), 
-                authorityItemSpecifier);
-        if (itemDocModel == null) {
-            throw new DocumentNotFoundException(String.format("Could not find authority item resource with CSID='%s'",
-                    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 = itemDocModel.getCurrentLifeCycleState();
-        String itemShortId = (String) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
-        
-        //
-        // Now get the item's Authority (the parent) information
-        //
-        DocumentModel authorityDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName,
-                authorityItemSpecifier.getParentSpecifier());
-        String authorityShortId = (String) NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
-        String localParentCsid = authorityDocModel.getName();
-        String remoteClientConfigName = (String)NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME);
-
-        //
-        // Using the short IDs of the local authority and item, create URN specifiers and retrieve the SAS authority item
-        //
-        AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authorityShortId, itemShortId);
-        // Get the shared authority server's copy
-        PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier, 
-                remoteClientConfigName, getAuthorityServicePath(), getEntityResponseType(), AuthorityClient.DONT_INCLUDE_RELATIONS);
-        Long sasRev = getRevision(sasPayloadIn);
-        String sasWorkflowState = getWorkflowState(sasPayloadIn);
-        //
-        // If the shared authority item is newer, update our local copy
-        //
-        if (sasRev > localItemRev || localIsProposed || ctx.shouldForceSync()) {
-            sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter the domain name part of any and all refnames in the payload
-            AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath());
-            PoxPayloadOut payloadOut = authorityResource.updateAuthorityItem(ctx, 
-                    ctx.getResourceMap(),                     
-                    ctx.getUriInfo(),
-                    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.NOT_PROPOSED,        // The items is not proposed, make it a real SAS item now
-                    AuthorityServiceUtils.SAS_ITEM);        // Since we're sync'ing, this must be a SAS item
-            if (payloadOut != null) {    
-                ctx.setOutput(payloadOut);
-                result = true;
-            }
-        }
-        //
-        // Check to see if we need to update the local items's workflow state to reflect that of the remote's
-        //
-        List<String> transitionList = getTransitionList(sasWorkflowState, localItemWorkflowState);
-        if (transitionList.isEmpty() == false) {
-            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.  This might involve multiple transitions.
-            //
-            for (String transition:transitionList) {
-                try {
-                    authorityResource.updateItemWorkflowWithTransition(ctx, localParentCsid, localItemCsid, transition, AuthorityServiceUtils.DONT_UPDATE_REV);
-                } catch (DocumentReferenceException de) {
-                    //
-                    // This exception means we tried unsuccessfully to soft-delete (workflow transition 'delete') an item that still has references to it from other records.
-                    //
-                    AuthorityServiceUtils.setAuthorityItemDeprecated(ctx, itemDocModel, authorityItemCommonSchemaName, AuthorityServiceUtils.DEPRECATED);  // Since we can't sof-delete it, we need to mark it as deprecated since it is soft-deleted on the SAS
-                    logger.warn(String.format("Could not transition item CSID='%s' from workflow state '%s' to '%s'.  Check the services log file for details.",
-                            localItemCsid, localItemWorkflowState, sasWorkflowState));
-                }
-            }
-            result = true;
-        }
-        
-        return result;
-    }
-    
-    /**
-     * We need to change the local item's state to one that maps to the replication server's workflow
-     * state.  This might involve making multiple transitions.
-     * 
-     * WIKI:
-     *     See table at https://collectionspace.atlassian.net/wiki/spaces/SDR/pages/665886940/Workflow+transitions+to+map+SAS+item+states+to+Local+item+states
-     *     (was https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564)
-     * 
-     */
-    private List<String> getTransitionList(String sasWorkflowState, String localItemWorkflowState) throws DocumentException {
-        List<String> result = new ArrayList<String>();        
-        //
-        // The first set of conditions maps a replication-server "project" state to a local client state of "replicated"
-        //
-        if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
-            // Do nothing.  We're good with this state
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-        //
-        // The second set of conditions maps a replication-server "deleted" state to a local client state of "deleted"
-        //
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
-            // Do nothing.  We're good with this state
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-        //
-        // The third set of conditions maps a replication-server "replicated" state to a local state of "replicated"
-        //
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
-            // Do nothing.  We're good with this state
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-        //
-        // The fourth set of conditions maps a replicatation-server "deprecated" state to a local state of "replicated_deprecated"
-        //
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
-            // Do nothing.
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
-        //
-        // The last set of conditions maps a replication-server "replicated_deleted" state to a local client state of "deleted"
-        //
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
-            // Do nothing.  We're good with this state
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-            result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
-        } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
-            result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
-        } else {
-            //
-            // If we get here, we've encountered a SAS workflow state that we don't recognize.
-            //
-            throw new DocumentException(String.format("Encountered an invalid workflow state of '%s' on a SAS authority item.", sasWorkflowState));
-        }
-        
-        return result;
-    }
-    
-    /* (non-Javadoc)
-     * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleCreate(org.collectionspace.services.common.document.DocumentWrapper)
-     */
-    @Override
-    public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
-        // first fill all the parts of the document, refname and short ID get set as well
-        super.handleCreate(wrapDoc);
-        // Ensure we have required fields set properly
-        handleInAuthority(wrapDoc.getWrappedObject());        
-    }
-
-    enum RefObjsSearchType {
-        ALL, NON_DELETED, DELETED_ONLY
-    }
-    
-    /*
-     * 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)
-     */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Override
-    public boolean handleDelete(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
-        boolean result = true;
-        
-        ServiceContext ctx = getServiceContext();
-        DocumentModel docModel = wrapDoc.getWrappedObject();
-        
-        AuthorityRefDocList refsToAllObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.ALL);
-        AuthorityRefDocList refsToSoftDeletedObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.DELETED_ONLY);
-        if (refsToAllObjects.getTotalItems() > 0) {
-            if (refsToAllObjects.getTotalItems() > refsToSoftDeletedObjects.getTotalItems()) {
-                //
-                // If the number of refs to active objects is greater than the number of refs to
-                // soft deleted objects then we can't delete the item.
-                //
-                logger.error(String.format("Cannot delete authority item CSID='%s' because it still has %d records in the system that are referencing it.",
-                        docModel.getName(), refsToSoftDeletedObjects.getTotalItems()));
-                if (logger.isWarnEnabled() == true) {
-                    logReferencingObjects(docModel, refsToAllObjects);
-                }
-
-                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()));
-            } else {
-                //
-                // If all the refs are to soft-deleted objects, we should soft-delete this authority item instead of hard-deleting it and instead of failing.
-                //
-                String parentCsid = (String) NuxeoUtils.getProperyValue(docModel, AuthorityItemJAXBSchema.IN_AUTHORITY);
-                String itemCsid = docModel.getName();
-                AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath());
-                authorityResource.updateItemWorkflowWithTransition(ctx, parentCsid, itemCsid, WorkflowClient.WORKFLOWTRANSITION_DELETE, 
-                        this.getShouldUpdateRevNumber());
-                result = false; // Don't delete since we just soft-deleted it.                
-            }
-        }
-        
-        //
-        // Since we've changed the state of the parent by deleting (or soft-deleting) one of its items, we might need to update the parent rev number
-        //
-        if (getShouldUpdateRevNumber() == true) {
-            updateRevNumbers(wrapDoc);
-        }
-        
-        return result;
-    }
-    
-    /**
-     * Checks to see if an authority item has referencing objects.
-     * 
-     * @param ctx
-     * @param docModel
-     * @return
-     * @throws Exception
-     */
-    @SuppressWarnings("rawtypes")
-    private AuthorityRefDocList getReferencingObjectsForStateTransitions(
-            ServiceContext ctx, 
-            DocumentModel docModel, 
-            RefObjsSearchType searchType) throws Exception {
-        AuthorityRefDocList referenceList = null;
-        
-        if (ctx.getUriInfo() == null) {
-            //
-            // We need a UriInfo object so we can pass "query" params to the AuthorityResource's getReferencingObjects() method
-            //
-            ctx.setUriInfo(this.getServiceContext().getUriInfo()); // try to get a UriInfo instance from the handler's context
-        }
-        
-        //
-        // Since the call to get referencing objects might indirectly use the WorkflowClient.WORKFLOW_QUERY_NONDELETED query param, we need to
-        // temporarily remove that query param if it is set.  If set, we'll save the value and reset once we're finished.
-        //
-        boolean doesContainValue = ctx.getUriInfo().getQueryParameters().containsKey(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
-        String previousValue = ctx.getUriInfo().getQueryParameters().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
-        
-        try {
-            if (doesContainValue) {
-                ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
-            }
-            AuthorityResource authorityResource = (AuthorityResource)ctx.getResource(getAuthorityServicePath());
-            referenceList = getReferencingObjects(authorityResource, ctx, docModel, searchType, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true); // useDefaultOrderByClause=true, computeTotal=true
-        } finally {
-            if (doesContainValue) {
-                ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, previousValue);
-            }
-        }
-        
-        return referenceList;
-    }
-    
-    @SuppressWarnings("rawtypes")
-    private AuthorityRefDocList getReferencingObjectsForMarkingTerm(
-            ServiceContext ctx, 
-            DocumentModel docModel, 
-            RefObjsSearchType searchType) throws Exception {
-        AuthorityRefDocList referenceList = null;
-        
-        if (ctx.getUriInfo() == null) {
-            //
-            // We need a UriInfo object so we can pass "query" params to the AuthorityResource's getReferencingObjects() method
-            //
-            ctx.setUriInfo(this.getServiceContext().getUriInfo()); // try to get a UriInfo instance from the handler's context
-        }
-        
-        //
-        // Since the call to get referencing objects might indirectly use the WorkflowClient.WORKFLOW_QUERY_NONDELETED query param, we need to
-        // temporarily remove that query param if it is set.  If set, we'll save the value and reset once we're finished.
-        //
-        boolean doesContainValue = ctx.getUriInfo().getQueryParameters().containsKey(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
-        String previousValue = ctx.getUriInfo().getQueryParameters().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
-        
-        try {
-            if (doesContainValue) {
-                ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
-            }
-            AuthorityResource authorityResource = (AuthorityResource)ctx.getResource(getAuthorityServicePath());
-            referenceList = getReferencingObjects(authorityResource, ctx, docModel, searchType, 0, 1, false, false);  // pageNum=0, pageSize=1, useDefaultOrderClause=false, computeTotal=false
-        } finally {
-            if (doesContainValue) {
-                ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, previousValue);
-            }
-        }
-        
-        return referenceList;
-    }    
-    
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private AuthorityRefDocList getReferencingObjects(
-            AuthorityResource authorityResource,
-            ServiceContext ctx,
-            DocumentModel docModel,
-            RefObjsSearchType searchType,
-            Integer pageNum, 
-            Integer pageSize,
-            boolean useDefaultOrderByClause,
-            boolean computeTotal) throws Exception {
-        AuthorityRefDocList result = null;
-        
-        String inAuthorityCsid = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
-        String itemCsid = docModel.getName();
-        
-        try {
-            switch (searchType) {
-                case ALL:
-                    // By default, get get everything
-                    break;
-                case NON_DELETED:
-                    // Get only non-deleted objects
-                    ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, Boolean.FALSE.toString());  // Add the wf_deleted=false query param to exclude soft-deleted items
-                    break;
-                case DELETED_ONLY:
-                    // Get only deleted objects
-                    ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP, Boolean.TRUE.toString());  // Add the wf_only_deleted query param to get only soft-deleted items
-                    break;
-            }
-            result = authorityResource.getReferencingObjects(ctx, inAuthorityCsid, itemCsid, ctx.getUriInfo(), pageNum, pageSize, useDefaultOrderByClause, computeTotal);
-
-        } finally {
-            //
-            // Cleanup query params
-            //
-            switch (searchType) {
-                case ALL:
-                    break;
-                case NON_DELETED:
-                    ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOWSTATE_DELETED);
-                    break;
-                case DELETED_ONLY:
-                    ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP);
-                    break;
-            }
-        }
-
-        return result;
-    }    
-    
-    private void logReferencingObjects(DocumentModel docModel, AuthorityRefDocList refObjs) {
-        List<AuthorityRefDocList.AuthorityRefDocItem> items = refObjs.getAuthorityRefDocItem();
-        logger.warn(String.format("The authority item CSID='%s' has the following references:", docModel.getName()));
-        int i = 0;
-        for (AuthorityRefDocList.AuthorityRefDocItem item : items) {
-            if (item.getWorkflowState().contains(WorkflowClient.WORKFLOWSTATE_DELETED) == false) {
-                logger.warn(docModel.getName() + " referenced by : list-item[" + i + "] "
-                        + item.getDocType() + "("
-                        + item.getDocId() + ") Name:["
-                        + item.getDocName() + "] Number:["
-                        + item.getDocNumber() + "] in field:["
-                        + item.getSourceField() + "]");
-                i++;
-            }
-        }
-    }
-
-    /*
-     * 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.nuxeo.client.java.RemoteDocumentModelHandlerImpl#completeUpdate(org.collectionspace.services.common.document.DocumentWrapper)
-     */
-    public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
-        // Must call our super class' version first
-        super.completeUpdate(wrapDoc);
-        
-        //
-        // Look for and update authority references with the updated refName
-        //
-        if (hasRefNameUpdate() == true) {
-            // We have work to do.
-            if (logger.isDebugEnabled()) {
-                final String EOL = System.getProperty("line.separator");
-                logger.debug("Need to find and update references to authority item." + EOL
-                        + "   Old refName" + oldRefNameOnUpdate + EOL
-                        + "   New refName" + newRefNameOnUpdate);
-            }
-            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
-            RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = getRepositoryClient(ctx);
-            CoreSessionInterface repoSession = this.getRepositorySession();
-            
-            // Update all the existing records that have a field with the old refName in it
-            int nUpdated = RefNameServiceUtils.updateAuthorityRefDocs(ctx, repoClient, repoSession,
-                    oldRefNameOnUpdate, newRefNameOnUpdate, getRefPropName());
-            
-            // Finished so log a message.
-            if (logger.isDebugEnabled()) {
-                logger.debug("Updated " + nUpdated + " instances of oldRefName to newRefName");
-            }
-        }
-    }
-    
-    /*
-     * Note that the Vocabulary service's document-model for items overrides this method.
-     */
-    protected String getPrimaryDisplayName(DocumentModel docModel, String schema,
-            String complexPropertyName, String fieldName) {
-        String result = null;
-
-        result = getStringValueInPrimaryRepeatingComplexProperty(docModel, schema, complexPropertyName, fieldName);
-        
-        return result;
-    }
-    
-    /* (non-Javadoc)
-     * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleUpdate(org.collectionspace.services.common.document.DocumentWrapper)
-     */
-    @Override
-    // FIXME: Once we remove the refName field from the authority item schemas, we can remove this override method since our super does everthing for us now.
-    @Deprecated
-    public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
-        // Must call our super's version first, this updates the core schema and the relationship records to deal with possible refName changes/update
-        super.handleUpdate(wrapDoc);
-        if (this.hasRefNameUpdate() == true) {
-            DocumentModel docModel = wrapDoc.getWrappedObject();
-            docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REF_NAME, this.newRefNameOnUpdate); // This field is deprecated since it is now a duplicate of what is in the collectionspace_core:refName field            
-        }
-    }
-    
-    //
-    // Handles both update calls (PUTS) AND create calls (POSTS)
-    //
-    public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
-        super.fillAllParts(wrapDoc, action);
-        DocumentModel documentModel = wrapDoc.getWrappedObject();
-
-        //
-        // Update the record's revision number on both CREATE and UPDATE actions (as long as it is NOT a SAS authority item)
-        //
-        Boolean propertyValue = (Boolean) documentModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SAS);
-        boolean isMarkedAsSASItem = propertyValue != null ? propertyValue : false;
-        if (this.getShouldUpdateRevNumber() == true && !isMarkedAsSASItem) { // We won't update rev numbers on synchronization with SAS items and on local changes to SAS items
-            updateRevNumbers(wrapDoc);
-        }
-        
-        if (getShouldUpdateSASFields() == true) {
-            //
-            // If this is a proposed item (not part of the SAS), mark it as such
-            //
-            documentModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.PROPOSED,
-                    new Boolean(this.getIsProposed()));
-            //
-            // If it is a SAS authority item, mark it as such
-            //
-            documentModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SAS,
-                    new Boolean(this.getIsSASItem()));
-        }
-    }
-    
-    /**
-     * Update the revision number of both the item and the item's parent.
-     * @param wrapDoc
-     * @throws Exception
-     */
-    protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
-        DocumentModel documentModel = wrapDoc.getWrappedObject();
-        Long rev = (Long)documentModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REV);
-        if (rev == null) {
-            rev = (long)0;
-        } else {
-            rev++;
-        }
-        documentModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REV, rev);
-        //
-        // Next, update the inAuthority (the parent's) rev number
-        //
-        String inAuthorityCsid = this.getInAuthorityCsid();
-        if (inAuthorityCsid == null) {
-            // When inAuthorityCsid is null, it usually means we're performing and update or synch with the SAS
-            inAuthorityCsid = (String)documentModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
-        }
-        DocumentModel inAuthorityDocModel = NuxeoUtils.getDocFromCsid(getServiceContext(), getRepositorySession(), inAuthorityCsid);
-        if (inAuthorityDocModel != null) {
-            Long parentRev = (Long)inAuthorityDocModel.getProperty(getParentCommonSchemaName(), AuthorityJAXBSchema.REV);
-            if (parentRev == null) {
-                parentRev = new Long(0);
-            }
-               parentRev++;
-               inAuthorityDocModel.setProperty(getParentCommonSchemaName(), AuthorityJAXBSchema.REV, parentRev);
-               getRepositorySession().saveDocument(inAuthorityDocModel);
-        } else {
-            logger.warn(String.format("Containing authority '%s' for item '%s' has been deleted.  Item is orphaned, so revision numbers can't be updated.",
-                    inAuthorityCsid, documentModel.getName()));
-        }
-    }    
-    
-    /**
-     * If no short identifier was provided in the input payload, generate a
-     * short identifier from the preferred term display name or term name.
-     */
-    private String handleDisplayNameAsShortIdentifier(DocumentModel docModel) throws Exception {
-        String result = (String) docModel.getProperty(authorityItemCommonSchemaName,
-                AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
-
-        if (Tools.isEmpty(result)) {
-            String termDisplayName = getPrimaryDisplayName(
-                    docModel, authorityItemCommonSchemaName,
-                    getItemTermInfoGroupXPathBase(),
-                    AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
-
-            String termName = getPrimaryDisplayName(
-                    docModel, authorityItemCommonSchemaName,
-                    getItemTermInfoGroupXPathBase(),
-                    AuthorityItemJAXBSchema.TERM_NAME);
-
-            String generatedShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(termDisplayName,
-                            termName);
-            docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER,
-                    generatedShortIdentifier);
-            result = generatedShortIdentifier;
-        }
-        
-        return result;
-    }
-
-    /**
-     * Generate a refName for the authority item from the short identifier
-     * and display name.
-     * 
-     * All refNames for authority items are generated.  If a client supplies
-     * a refName, it will be overwritten during create (per this method) 
-     * or discarded during update (per filterReadOnlyPropertiesForPart).
-     * 
-     * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
-     * 
-     */
-    protected String updateRefnameForAuthorityItem(DocumentModel docModel,
-            String schemaName) throws Exception {
-        String result = null;
-        
-        RefName.RefNameInterface refname = getRefName(getServiceContext(), docModel);
-        String refNameStr = refname.toString();
-        docModel.setProperty(schemaName, AuthorityItemJAXBSchema.REF_NAME, refNameStr);
-        result = refNameStr;
-        
-        return result;
-    }
-
-    /**
-     * Check the logic around the parent pointer. Note that we only need do this on
-     * create, since we have logic to make this read-only on update. 
-     * 
-     * @param docModel
-     * 
-     * @throws Exception the exception
-     */
-    private void handleInAuthority(DocumentModel docModel) throws Exception {
-        if (inAuthority == null) { // Only happens on queries to wildcarded authorities
-            throw new IllegalStateException("Trying to Create an object with no inAuthority value!");
-        }
-        docModel.setProperty(authorityItemCommonSchemaName, 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,
-            List<String> serviceTypes,
-            String propertyName,
-            String itemcsid,
-            Integer pageNum,
-            Integer pageSize,
-            boolean useDefaultOrderByClause,
-            boolean computeTotal) throws Exception {
-        AuthorityRefDocList authRefDocList = null;
-        CoreSessionInterface repoSession = (CoreSessionInterface) ctx.getCurrentRepositorySession();
-        boolean releaseRepoSession = false;
-        
-        try {
-            NuxeoRepositoryClientImpl repoClient = (NuxeoRepositoryClientImpl)this.getRepositoryClient(ctx);
-            repoSession = this.getRepositorySession();
-            if (repoSession == null) {
-                repoSession = repoClient.getRepositorySession(ctx);
-                releaseRepoSession = true;
-            }
-            DocumentFilter myFilter = getDocumentFilter();
-            if (pageSize != null) {
-                myFilter.setPageSize(pageSize);
-            }
-            if (pageNum != null) {
-                myFilter.setStartPage(pageNum);
-            }
-            myFilter.setUseDefaultOrderByClause(useDefaultOrderByClause);
-
-            try {
-                DocumentWrapper<DocumentModel> wrapper = repoClient.getDoc(repoSession, ctx, itemcsid);
-                DocumentModel docModel = wrapper.getWrappedObject();
-                String refName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityItemJAXBSchema.REF_NAME); //docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
-                authRefDocList = RefNameServiceUtils.getAuthorityRefDocs(
-                        repoSession, 
-                        ctx, 
-                        repoClient,
-                        serviceTypes,
-                        refName,
-                        propertyName,
-                        myFilter, 
-                        useDefaultOrderByClause,
-                        computeTotal /*computeTotal*/);
-            } catch (PropertyException pe) {
-                throw pe;
-            } catch (DocumentException de) {
-                throw de;
-            } catch (Exception e) {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Caught exception ", e);
-                }
-                throw new DocumentException(e);
-            } finally {
-                // If we got/aquired a new seesion then we're responsible for releasing it.
-                if (releaseRepoSession && repoSession != null) {
-                    repoClient.releaseRepositorySession(ctx, repoSession);
-                }
-            }
-        } catch (Exception e) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Caught exception ", e);
-            }
-            throw new DocumentException(e);
-        }
-        
-        return authRefDocList;
-    }
-
-    /* (non-Javadoc)
-     * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
-     */
-    @Override
-    protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
-            throws Exception {
-        Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
-
-        // Add the CSID to the common part, since they may have fetched via the shortId.
-        if (partMeta.getLabel().equalsIgnoreCase(authorityItemCommonSchemaName)) {
-            String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
-            unQObjectProperties.put("csid", csid);
-        }
-
-        return unQObjectProperties;
-    }
-    
-    /**
-     * Filters out selected values supplied in an update request.
-     * 
-     * For example, filters out AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure
-     * that the link to the item's parent remains untouched.
-     * 
-     * @param objectProps the properties filtered out from the update payload
-     * @param partMeta metadata for the object to fill
-     */
-    @Override
-    public void filterReadOnlyPropertiesForPart(
-            Map<String, Object> objectProps, ObjectPartType partMeta) {
-        super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
-        String commonPartLabel = getServiceContext().getCommonPartLabel();
-        if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
-            objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
-            objectProps.remove(AuthorityItemJAXBSchema.CSID);
-            if (getServiceContext().shouldForceUpdateRefnameReferences() == false) {
-               objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
-            }
-            objectProps.remove(AuthorityItemJAXBSchema.REF_NAME);
-        }
-    }
-    
-    /**
-     * Returns the items in a list of term display names whose names contain
-     * a partial term (as might be submitted in a search query, for instance).
-     * @param termDisplayNameList a list of term display names.
-     * @param partialTerm a partial term display name; that is, a portion
-     * of a display name that might be expected to match 0-n terms in the list.
-     * @return a list of term display names that matches the partial term.
-     * Matches are case-insensitive. As well, before matching is performed, any
-     * special-purpose characters that may appear in the partial term (such as
-     * wildcards and anchor characters) are filtered out from both compared terms.
-     */
-    protected List<String> getPartialTermDisplayNameMatches(List<String> termDisplayNameList, String partialTerm) {
-        List<String> result = new ArrayList<>();
-        String partialTermMatchExpression = filterAnchorAndWildcardChars(partialTerm).toLowerCase();
-        try {
-            for (String termDisplayName : termDisplayNameList) {
-                if (termDisplayName.toLowerCase()
-                        .matches(partialTermMatchExpression) == true) {
-                        result.add(termDisplayName);
-                }
-            }
-        } catch (PatternSyntaxException pse) {
-            logger.warn("Error in regex match pattern '%s' for term display names: %s",
-                    partialTermMatchExpression, pse.getMessage());
-        }
-        return result;
-    }
-    
-    /**
-     * Filters user-supplied anchor and wildcard characters in a string,
-     * replacing them with equivalent regular expressions.
-     * @param term a term in which to filter anchor and wildcard characters.
-     * @return the term with those characters filtered.
-     */
-    protected String filterAnchorAndWildcardChars(String term) {
-        if (Tools.isBlank(term)) {
-            return term;
-        }
-        if (term.length() < 3) {
-            return term;
-        }
-        if (logger.isTraceEnabled()) {
-            logger.trace(String.format("Term = %s", term));
-        }
-        Boolean anchorAtStart = false;
-        Boolean anchorAtEnd = false;
-        String filteredTerm;
-        StringBuilder filteredTermBuilder = new StringBuilder(term);
-        // Term contains no anchor or wildcard characters.
-        if ( (! term.contains(NuxeoRepositoryClientImpl.USER_SUPPLIED_ANCHOR_CHAR))
-                && (! term.contains(NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD)) ) {
-            filteredTerm = term;
-        } else {
-            // Term contains at least one such character.
-            try {
-                // Filter the starting anchor or wildcard character, if any.
-                String firstChar = filteredTermBuilder.substring(0,1);
-                switch (firstChar) {
-                    case NuxeoRepositoryClientImpl.USER_SUPPLIED_ANCHOR_CHAR:
-                        anchorAtStart = true;
-                        break;
-                    case NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD:
-                        filteredTermBuilder.deleteCharAt(0);
-                        break;
-                }
-                if (logger.isTraceEnabled()) {
-                    logger.trace(String.format("After first char filtering = %s", filteredTermBuilder.toString()));
-                }
-                // Filter the ending anchor or wildcard character, if any.
-                int lastPos = filteredTermBuilder.length() - 1;
-                String lastChar = filteredTermBuilder.substring(lastPos);
-                switch (lastChar) {
-                    case NuxeoRepositoryClientImpl.USER_SUPPLIED_ANCHOR_CHAR:
-                        filteredTermBuilder.deleteCharAt(lastPos);
-                        filteredTermBuilder.insert(filteredTermBuilder.length(), NuxeoRepositoryClientImpl.ENDING_ANCHOR_CHAR);
-                        anchorAtEnd = true;
-                        break;
-                    case NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD:
-                        filteredTermBuilder.deleteCharAt(lastPos);
-                        break;
-                }
-                if (logger.isTraceEnabled()) {
-                    logger.trace(String.format("After last char filtering = %s", filteredTermBuilder.toString()));
-                }
-                filteredTerm = filteredTermBuilder.toString();
-                // Filter all other wildcards, if any.
-                filteredTerm = filteredTerm.replaceAll(NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD_REGEX, ZERO_OR_MORE_ANY_CHAR_REGEX);
-                if (logger.isTraceEnabled()) {
-                    logger.trace(String.format("After replacing user wildcards = %s", filteredTerm));
-                }
-            } catch (Exception e) {
-                logger.warn(String.format("Error filtering anchor and wildcard characters from string: %s", e.getMessage()));
-                return term;
-            }
-        }
-        // Wrap the term in beginning and ending regex wildcards, unless a
-        // starting or ending anchor character was present.
-        return (anchorAtStart ? "" : ZERO_OR_MORE_ANY_CHAR_REGEX)
-                + filteredTerm
-                + (anchorAtEnd ? "" : ZERO_OR_MORE_ANY_CHAR_REGEX);
-    }
-    
-    @SuppressWarnings("unchecked")
-    private List<String> getPartialTermDisplayNameMatches(DocumentModel docModel, // REM - CSPACE-5133
-            String schema, ListResultField field, String partialTerm) {
-        List<String> result = null;
-          
-        String xpath = field.getXpath(); // results in something like "persons_common:personTermGroupList/[0]/termDisplayName"
-        int endOfTermGroup = xpath.lastIndexOf("/[0]/");
-        String propertyName = endOfTermGroup != -1 ? xpath.substring(0, endOfTermGroup) : xpath; // it may not be multivalued so the xpath passed in would be the property name
-        Object value = null;
-        
-        try {
-            value = docModel.getProperty(schema, propertyName);
-        } catch (Exception e) {
-            logger.error("Could not extract term display name with property = "
-                    + propertyName, e);
-        }
-        
-        if (value != null && value instanceof ArrayList) {
-            ArrayList<HashMap<String, Object>> termGroupList = (ArrayList<HashMap<String, Object>>)value;
-            int arrayListSize = termGroupList.size();
-            if (arrayListSize > 1) { // if there's only 1 element in the list then we've already matched the primary term's display name
-                List<String> displayNameList = new ArrayList<String>();
-                for (int i = 1; i < arrayListSize; i++) { // start at 1, skip the primary term's displayName since we will always return it
-                    HashMap<String, Object> map = (HashMap<String, Object>)termGroupList.get(i);
-                    String termDisplayName = (String) map.get(AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
-                    displayNameList.add(i - 1, termDisplayName);
-                }
-                
-                result = getPartialTermDisplayNameMatches(displayNameList, partialTerm);
-            }
-        }
-
-        return result;
-    }
-    
-    private boolean isTermReferenced(DocumentModel docModel) throws Exception {
-        boolean result = false;
-        
-        AuthorityRefDocList referenceList = null;
-
-        String wf_deletedStr = (String) getServiceContext().getQueryParams().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
-        if (wf_deletedStr != null && Tools.isFalse(wf_deletedStr)) {
-            //
-            // if query param 'wf_deleted=false', we won't count references to soft-deleted records
-            //
-            referenceList = getReferencingObjectsForMarkingTerm(getServiceContext(), docModel, RefObjsSearchType.NON_DELETED);
-        } else {
-            //
-            // if query param 'wf_deleted=true' or missing, we count references to soft-deleted and active records
-            //
-            referenceList = getReferencingObjectsForMarkingTerm(getServiceContext(), docModel, RefObjsSearchType.ALL);
-        }
-        
-        if (referenceList.getTotalItems() > 0) {
-            result = true;
-        }
-        
-        return result;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
-            String schema, ListResultField field) throws DocumentException {
-        Object result = null;
-        String fieldXPath = field.getXpath();
-        
-        if (fieldXPath.equalsIgnoreCase(AuthorityClient.REFERENCED) == false) {
-            result = NuxeoUtils.getXPathValue(docModel, schema, field.getXpath());
-        } else {
-            //
-            // Check to see if the request is asking us to mark terms as referenced or not.
-            //
-            String markIfReferencedStr = (String) getServiceContext().getQueryParams().getFirst(AuthorityClient.MARK_IF_REFERENCED_QP);
-            if (Tools.isTrue(markIfReferencedStr) == false) {
-                return null; // leave the <referenced> element as null since they're not asking for it
-            } else try {
-                return Boolean.toString(isTermReferenced(docModel)); // set the <referenced> element to either 'true' or 'false'
-            } catch (Exception e) {
-                String msg = String.format("Failed while trying to find records referencing term CSID='%s'.", docModel.getName());
-                throw new DocumentException(msg, e);
-            }
-        }
-                
-        //
-        // Special handling of list item values for authority items (only)
-        // takes place here:
-        //
-        // If the list result field is the termDisplayName element,
-        // check whether a partial term matching query was made.
-        // If it was, emit values for both the preferred (aka primary)
-        // term and for all non-preferred terms, if any.
-        //
-        String elName = field.getElement();
-        if (isTermDisplayName(elName) == true) {
-            MultivaluedMap<String, String> queryParams = this.getServiceContext().getQueryParams();
-            String partialTerm = queryParams != null ? queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM) : null;
-            if (partialTerm != null && partialTerm.trim().isEmpty() == false) {
-                String primaryTermDisplayName = (String)result;
-                List<String> matches = getPartialTermDisplayNameMatches(docModel, schema, field, partialTerm);
-                if (matches != null && matches.isEmpty() == false) {
-                    matches.add(0, primaryTermDisplayName); // insert the primary term's display name at the beginning of the list
-                    result = matches; // set the result to a list of matching term display names with the primary term's display name at the beginning
-                }
-            }
-        }
-        
-        return result;
-    }
-    
-    @Override
-    public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
-        MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
-        super.extractAllParts(wrapDoc);
-    }
-
-    protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList) {
-        List<RelationsCommonList.RelationListItem> result = newRelationsCommonList();
-        for (RelationsCommonList.RelationListItem item : inboundList) {
-            result.add(item);
-        }
-        return result;
-    }
-
-
-    /* don't even THINK of re-using this method.
-     * String example_uri = "/locationauthorities/7ec60f01-84ab-4908-9a6a/items/a5466530-713f-43b4-bc05";
-     */
-    @Deprecated
-    private String extractInAuthorityCSID(String uri) {
-        String IN_AUTHORITY_REGEX = "/(.*?)/(.*?)/(.*)";
-        Pattern p = Pattern.compile(IN_AUTHORITY_REGEX);
-        Matcher m = p.matcher(uri);
-        if (m.find()) {
-            if (m.groupCount() < 3) {
-                logger.warn("REGEX-WRONG-GROUPCOUNT looking in " + uri);
-                return "";
-            } else {
-                //String service = m.group(1);
-                String inauth = m.group(2);
-                //String theRest = m.group(3);
-                return inauth;
-                //print("service:"+service+", inauth:"+inauth+", rest:"+rest);
-            }
-        } else {
-            logger.warn("REGEX-NOT-MATCHED looking in " + uri);
-            return "";
-        }
-    }
-
-    //ensures CSPACE-4042
-    protected void uriPointsToSameAuthority(String thisURI, String inboundItemURI) throws Exception {
-        String authorityCSID = extractInAuthorityCSID(thisURI);
-        String authorityCSIDForInbound = extractInAuthorityCSID(inboundItemURI);
-        if (Tools.isBlank(authorityCSID)
-                || Tools.isBlank(authorityCSIDForInbound)
-                || (!authorityCSID.equalsIgnoreCase(authorityCSIDForInbound))) {
-            throw new Exception("Item URI " + thisURI + " must point to same authority as related item: " + inboundItemURI);
-        }
-    }
-
-    public String getItemTermInfoGroupXPathBase() {
-        return authorityItemTermGroupXPathBase;
-    }
-        
-    public void setItemTermInfoGroupXPathBase(String itemTermInfoGroupXPathBase) {
-        authorityItemTermGroupXPathBase = itemTermInfoGroupXPathBase;
-    }
-    
-    protected String getAuthorityItemCommonSchemaName() {
-        return authorityItemCommonSchemaName;
-    }
-    
-    // @Override
-    public boolean isJDBCQuery() {
-        boolean result = false;
-        
-        MultivaluedMap<String, String> queryParams = getServiceContext().getQueryParams();
-        //
-        // Look the query params to see if we need to make a SQL query.
-        //
-        String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
-        if (partialTerm != null && partialTerm.trim().isEmpty() == false) {
-            result = true;
-        }
-        
-        return result;
-    }
-    
-    // By convention, the name of the database table that contains
-    // repeatable term information group records is derivable from
-    // an existing XPath base value, by removing a suffix and converting
-    // to lowercase
-    protected String getTermGroupTableName() {
-        String termInfoGroupListName = getItemTermInfoGroupXPathBase();
-        return termInfoGroupListName.substring(0, termInfoGroupListName.lastIndexOf(LIST_SUFFIX)).toLowerCase();
-    }
-    
-    protected String getInAuthorityValue() {
-        String inAuthorityValue = getInAuthorityCsid();
-        if (Tools.notBlank(inAuthorityValue)) {
-            return inAuthorityValue;
-        } else {
-            return AuthorityResource.PARENT_WILDCARD;
-        }
-    }
-    
-    @Override
-    public Map<String,String> getJDBCQueryParams() {
-        // FIXME: Get all of the following values from appropriate external constants.
-        // At present, these are duplicated in both RepositoryClientImpl
-        // and in AuthorityItemDocumentModelHandler.
-        final String TERM_GROUP_LIST_NAME = "TERM_GROUP_LIST_NAME";
-        final String TERM_GROUP_TABLE_NAME_PARAM = "TERM_GROUP_TABLE_NAME";
-        final String IN_AUTHORITY_PARAM = "IN_AUTHORITY";
-        
-        Map<String,String> params = super.getJDBCQueryParams();
-        params.put(TERM_GROUP_LIST_NAME, getItemTermInfoGroupXPathBase());
-        params.put(TERM_GROUP_TABLE_NAME_PARAM, getTermGroupTableName());
-        params.put(IN_AUTHORITY_PARAM, getInAuthorityValue());
-        return params;
-    }
-    
+               boolean result = false;
+               ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
+
+               //
+               // Get information about the local authority item so we can compare with corresponding item on the shared authority server
+               //
+               AuthorityItemSpecifier authorityItemSpecifier = (AuthorityItemSpecifier) wrapDoc.getWrappedObject();
+               DocumentModel itemDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), getAuthorityItemCommonSchemaName(),
+                               authorityItemSpecifier);
+               if (itemDocModel == null) {
+                       throw new DocumentNotFoundException(String.format("Could not find authority item resource with CSID='%s'",
+                                       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 = itemDocModel.getCurrentLifeCycleState();
+               String itemShortId = (String) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
+
+               //
+               // Now get the item's Authority (the parent) information
+               //
+               DocumentModel authorityDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName,
+                               authorityItemSpecifier.getParentSpecifier());
+               String authorityShortId = (String) NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
+               String localParentCsid = authorityDocModel.getName();
+               String remoteClientConfigName = (String)NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME);
+
+               //
+               // Using the short IDs of the local authority and item, create URN specifiers and retrieve the SAS authority item
+               //
+               AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authorityShortId, itemShortId);
+               // Get the shared authority server's copy
+               PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier,
+                               remoteClientConfigName, getAuthorityServicePath(), getEntityResponseType(), AuthorityClient.DONT_INCLUDE_RELATIONS);
+               Long sasRev = getRevision(sasPayloadIn);
+               String sasWorkflowState = getWorkflowState(sasPayloadIn);
+               //
+               // If the shared authority item is newer, update our local copy
+               //
+               if (sasRev > localItemRev || localIsProposed || ctx.shouldForceSync()) {
+                       sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter the domain name part of any and all refnames in the payload
+                       AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath());
+                       PoxPayloadOut payloadOut = authorityResource.updateAuthorityItem(ctx,
+                                       ctx.getResourceMap(),
+                                       ctx.getUriInfo(),
+                                       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.NOT_PROPOSED,             // The items is not proposed, make it a real SAS item now
+                                       AuthorityServiceUtils.SAS_ITEM);                // Since we're sync'ing, this must be a SAS item
+                       if (payloadOut != null) {
+                               ctx.setOutput(payloadOut);
+                               result = true;
+                       }
+               }
+               //
+               // Check to see if we need to update the local items's workflow state to reflect that of the remote's
+               //
+               List<String> transitionList = getTransitionList(sasWorkflowState, localItemWorkflowState);
+               if (transitionList.isEmpty() == false) {
+                       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.  This might involve multiple transitions.
+                       //
+                       for (String transition:transitionList) {
+                               try {
+                                       authorityResource.updateItemWorkflowWithTransition(ctx, localParentCsid, localItemCsid, transition, AuthorityServiceUtils.DONT_UPDATE_REV);
+                               } catch (DocumentReferenceException de) {
+                                       //
+                                       // This exception means we tried unsuccessfully to soft-delete (workflow transition 'delete') an item that still has references to it from other records.
+                                       //
+                                       AuthorityServiceUtils.setAuthorityItemDeprecated(ctx, itemDocModel, authorityItemCommonSchemaName, AuthorityServiceUtils.DEPRECATED);  // Since we can't sof-delete it, we need to mark it as deprecated since it is soft-deleted on the SAS
+                                       logger.warn(String.format("Could not transition item CSID='%s' from workflow state '%s' to '%s'.  Check the services log file for details.",
+                                                       localItemCsid, localItemWorkflowState, sasWorkflowState));
+                               }
+                       }
+                       result = true;
+               }
+
+               return result;
+       }
+
+       /**
+        * We need to change the local item's state to one that maps to the replication server's workflow
+        * state.  This might involve making multiple transitions.
+        *
+        * WIKI:
+        *       See table at https://collectionspace.atlassian.net/wiki/spaces/SDR/pages/665886940/Workflow+transitions+to+map+SAS+item+states+to+Local+item+states
+        *       (was https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564)
+        *
+        */
+       private List<String> getTransitionList(String sasWorkflowState, String localItemWorkflowState) throws DocumentException {
+               List<String> result = new ArrayList<String>();
+               //
+               // The first set of conditions maps a replication-server "project" state to a local client state of "replicated"
+               //
+               if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
+                       // Do nothing.  We're good with this state
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+               //
+               // The second set of conditions maps a replication-server "deleted" state to a local client state of "deleted"
+               //
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
+                       // Do nothing.  We're good with this state
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+               //
+               // The third set of conditions maps a replication-server "replicated" state to a local state of "replicated"
+               //
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
+                       // Do nothing.  We're good with this state
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+               //
+               // The fourth set of conditions maps a replicatation-server "deprecated" state to a local state of "replicated_deprecated"
+               //
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
+                       // Do nothing.
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
+               //
+               // The last set of conditions maps a replication-server "replicated_deleted" state to a local client state of "deleted"
+               //
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
+                       // Do nothing.  We're good with this state
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
+               } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
+                       result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
+               } else {
+                       //
+                       // If we get here, we've encountered a SAS workflow state that we don't recognize.
+                       //
+                       throw new DocumentException(String.format("Encountered an invalid workflow state of '%s' on a SAS authority item.", sasWorkflowState));
+               }
+
+               return result;
+       }
+
+       /* (non-Javadoc)
+        * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleCreate(org.collectionspace.services.common.document.DocumentWrapper)
+        */
+       @Override
+       public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+               // first fill all the parts of the document, refname and short ID get set as well
+               super.handleCreate(wrapDoc);
+               // Ensure we have required fields set properly
+               handleInAuthority(wrapDoc.getWrappedObject());
+       }
+
+       enum RefObjsSearchType {
+               ALL, NON_DELETED, DELETED_ONLY
+       }
+
+       /*
+        * 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)
+        */
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       @Override
+       public boolean handleDelete(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+               boolean result = true;
+
+               ServiceContext ctx = getServiceContext();
+               DocumentModel docModel = wrapDoc.getWrappedObject();
+
+               AuthorityRefDocList refsToAllObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.ALL);
+               AuthorityRefDocList refsToSoftDeletedObjects = getReferencingObjectsForStateTransitions(ctx, docModel, RefObjsSearchType.DELETED_ONLY);
+               if (refsToAllObjects.getTotalItems() > 0) {
+                       if (refsToAllObjects.getTotalItems() > refsToSoftDeletedObjects.getTotalItems()) {
+                               //
+                               // If the number of refs to active objects is greater than the number of refs to
+                               // soft deleted objects then we can't delete the item.
+                               //
+                               logger.error(String.format("Cannot delete authority item CSID='%s' because it still has %d records in the system that are referencing it.",
+                                               docModel.getName(), refsToSoftDeletedObjects.getTotalItems()));
+                               if (logger.isWarnEnabled() == true) {
+                                       logReferencingObjects(docModel, refsToAllObjects);
+                               }
+
+                               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()));
+                       } else {
+                               //
+                               // If all the refs are to soft-deleted objects, we should soft-delete this authority item instead of hard-deleting it and instead of failing.
+                               //
+                               String parentCsid = (String) NuxeoUtils.getProperyValue(docModel, AuthorityItemJAXBSchema.IN_AUTHORITY);
+                               String itemCsid = docModel.getName();
+                               AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath());
+                               authorityResource.updateItemWorkflowWithTransition(ctx, parentCsid, itemCsid, WorkflowClient.WORKFLOWTRANSITION_DELETE,
+                                               this.getShouldUpdateRevNumber());
+                               result = false; // Don't delete since we just soft-deleted it.
+                       }
+               }
+
+               //
+               // Since we've changed the state of the parent by deleting (or soft-deleting) one of its items, we might need to update the parent rev number
+               //
+               if (getShouldUpdateRevNumber() == true) {
+                       updateRevNumbers(wrapDoc);
+               }
+
+               return result;
+       }
+
+       /**
+        * Checks to see if an authority item has referencing objects.
+        *
+        * @param ctx
+        * @param docModel
+        * @return
+        * @throws Exception
+        */
+       @SuppressWarnings("rawtypes")
+       private AuthorityRefDocList getReferencingObjectsForStateTransitions(
+                       ServiceContext ctx,
+                       DocumentModel docModel,
+                       RefObjsSearchType searchType) throws Exception {
+               AuthorityRefDocList referenceList = null;
+
+               if (ctx.getUriInfo() == null) {
+                       //
+                       // We need a UriInfo object so we can pass "query" params to the AuthorityResource's getReferencingObjects() method
+                       //
+                       ctx.setUriInfo(this.getServiceContext().getUriInfo()); // try to get a UriInfo instance from the handler's context
+               }
+
+               //
+               // Since the call to get referencing objects might indirectly use the WorkflowClient.WORKFLOW_QUERY_NONDELETED query param, we need to
+               // temporarily remove that query param if it is set.  If set, we'll save the value and reset once we're finished.
+               //
+               boolean doesContainValue = ctx.getUriInfo().getQueryParameters().containsKey(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+               String previousValue = ctx.getUriInfo().getQueryParameters().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+
+               try {
+                       if (doesContainValue) {
+                               ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+                       }
+                       AuthorityResource authorityResource = (AuthorityResource)ctx.getResource(getAuthorityServicePath());
+                       referenceList = getReferencingObjects(authorityResource, ctx, docModel, searchType, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true); // useDefaultOrderByClause=true, computeTotal=true
+               } finally {
+                       if (doesContainValue) {
+                               ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, previousValue);
+                       }
+               }
+
+               return referenceList;
+       }
+
+       @SuppressWarnings("rawtypes")
+       private AuthorityRefDocList getReferencingObjectsForMarkingTerm(
+                       ServiceContext ctx,
+                       DocumentModel docModel,
+                       RefObjsSearchType searchType) throws Exception {
+               AuthorityRefDocList referenceList = null;
+
+               if (ctx.getUriInfo() == null) {
+                       //
+                       // We need a UriInfo object so we can pass "query" params to the AuthorityResource's getReferencingObjects() method
+                       //
+                       ctx.setUriInfo(this.getServiceContext().getUriInfo()); // try to get a UriInfo instance from the handler's context
+               }
+
+               //
+               // Since the call to get referencing objects might indirectly use the WorkflowClient.WORKFLOW_QUERY_NONDELETED query param, we need to
+               // temporarily remove that query param if it is set.  If set, we'll save the value and reset once we're finished.
+               //
+               boolean doesContainValue = ctx.getUriInfo().getQueryParameters().containsKey(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+               String previousValue = ctx.getUriInfo().getQueryParameters().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+
+               try {
+                       if (doesContainValue) {
+                               ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+                       }
+                       AuthorityResource authorityResource = (AuthorityResource)ctx.getResource(getAuthorityServicePath());
+                       referenceList = getReferencingObjects(authorityResource, ctx, docModel, searchType, 0, 1, false, false);  // pageNum=0, pageSize=1, useDefaultOrderClause=false, computeTotal=false
+               } finally {
+                       if (doesContainValue) {
+                               ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, previousValue);
+                       }
+               }
+
+               return referenceList;
+       }
+
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       private AuthorityRefDocList getReferencingObjects(
+                       AuthorityResource authorityResource,
+                       ServiceContext ctx,
+                       DocumentModel docModel,
+                       RefObjsSearchType searchType,
+                       Integer pageNum,
+                       Integer pageSize,
+                       boolean useDefaultOrderByClause,
+                       boolean computeTotal) throws Exception {
+               AuthorityRefDocList result = null;
+
+               String inAuthorityCsid = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
+               String itemCsid = docModel.getName();
+
+               try {
+                       switch (searchType) {
+                               case ALL:
+                                       // By default, get get everything
+                                       break;
+                               case NON_DELETED:
+                                       // Get only non-deleted objects
+                                       ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, Boolean.FALSE.toString());  // Add the wf_deleted=false query param to exclude soft-deleted items
+                                       break;
+                               case DELETED_ONLY:
+                                       // Get only deleted objects
+                                       ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP, Boolean.TRUE.toString());  // Add the wf_only_deleted query param to get only soft-deleted items
+                                       break;
+                       }
+                       result = authorityResource.getReferencingObjects(ctx, inAuthorityCsid, itemCsid, ctx.getUriInfo(), pageNum, pageSize, useDefaultOrderByClause, computeTotal);
+
+               } finally {
+                       //
+                       // Cleanup query params
+                       //
+                       switch (searchType) {
+                               case ALL:
+                                       break;
+                               case NON_DELETED:
+                                       ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOWSTATE_DELETED);
+                                       break;
+                               case DELETED_ONLY:
+                                       ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP);
+                                       break;
+                       }
+               }
+
+               return result;
+       }
+
+       private void logReferencingObjects(DocumentModel docModel, AuthorityRefDocList refObjs) {
+               List<AuthorityRefDocList.AuthorityRefDocItem> items = refObjs.getAuthorityRefDocItem();
+               logger.warn(String.format("The authority item CSID='%s' has the following references:", docModel.getName()));
+               int i = 0;
+               for (AuthorityRefDocList.AuthorityRefDocItem item : items) {
+                       if (item.getWorkflowState().contains(WorkflowClient.WORKFLOWSTATE_DELETED) == false) {
+                               logger.warn(docModel.getName() + " referenced by : list-item[" + i + "] "
+                                               + item.getDocType() + "("
+                                               + item.getDocId() + ") Name:["
+                                               + item.getDocName() + "] Number:["
+                                               + item.getDocNumber() + "] in field:["
+                                               + item.getSourceField() + "]");
+                               i++;
+                       }
+               }
+       }
+
+       /*
+        * 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.nuxeo.client.java.RemoteDocumentModelHandlerImpl#completeUpdate(org.collectionspace.services.common.document.DocumentWrapper)
+        */
+       public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+               // Must call our super class' version first
+               super.completeUpdate(wrapDoc);
+
+               //
+               // Look for and update authority references with the updated refName
+               //
+               if (hasRefNameUpdate() == true) {
+                       // We have work to do.
+                       if (logger.isDebugEnabled()) {
+                               final String EOL = System.getProperty("line.separator");
+                               logger.debug("Need to find and update references to authority item." + EOL
+                                               + "   Old refName" + oldRefNameOnUpdate + EOL
+                                               + "   New refName" + newRefNameOnUpdate);
+                       }
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
+                       RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = getRepositoryClient(ctx);
+                       CoreSessionInterface repoSession = this.getRepositorySession();
+
+                       // Update all the existing records that have a field with the old refName in it
+                       int nUpdated = RefNameServiceUtils.updateAuthorityRefDocs(ctx, repoClient, repoSession,
+                                       oldRefNameOnUpdate, newRefNameOnUpdate, getRefPropName());
+
+                       // Finished so log a message.
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("Updated " + nUpdated + " instances of oldRefName to newRefName");
+                       }
+               }
+       }
+
+       /*
+        * Note that the Vocabulary service's document-model for items overrides this method.
+        */
+       protected String getPrimaryDisplayName(DocumentModel docModel, String schema,
+                       String complexPropertyName, String fieldName) {
+               String result = null;
+
+               result = getStringValueInPrimaryRepeatingComplexProperty(docModel, schema, complexPropertyName, fieldName);
+
+               return result;
+       }
+
+       /* (non-Javadoc)
+        * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleUpdate(org.collectionspace.services.common.document.DocumentWrapper)
+        */
+       @Override
+       // FIXME: Once we remove the refName field from the authority item schemas, we can remove this override method since our super does everthing for us now.
+       @Deprecated
+       public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+               // Must call our super's version first, this updates the core schema and the relationship records to deal with possible refName changes/update
+               super.handleUpdate(wrapDoc);
+               if (this.hasRefNameUpdate() == true) {
+                       DocumentModel docModel = wrapDoc.getWrappedObject();
+                       docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REF_NAME, this.newRefNameOnUpdate); // This field is deprecated since it is now a duplicate of what is in the collectionspace_core:refName field
+               }
+       }
+
+       //
+       // Handles both update calls (PUTS) AND create calls (POSTS)
+       //
+       public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
+               super.fillAllParts(wrapDoc, action);
+               DocumentModel documentModel = wrapDoc.getWrappedObject();
+
+               //
+               // Update the record's revision number on both CREATE and UPDATE actions (as long as it is NOT a SAS authority item)
+               //
+               Boolean propertyValue = (Boolean) documentModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SAS);
+               boolean isMarkedAsSASItem = propertyValue != null ? propertyValue : false;
+               if (this.getShouldUpdateRevNumber() == true && !isMarkedAsSASItem) { // We won't update rev numbers on synchronization with SAS items and on local changes to SAS items
+                       updateRevNumbers(wrapDoc);
+               }
+
+               if (getShouldUpdateSASFields() == true) {
+                       //
+                       // If this is a proposed item (not part of the SAS), mark it as such
+                       //
+                       documentModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.PROPOSED,
+                                       new Boolean(this.getIsProposed()));
+                       //
+                       // If it is a SAS authority item, mark it as such
+                       //
+                       documentModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SAS,
+                                       new Boolean(this.getIsSASItem()));
+               }
+       }
+
+       /**
+        * Update the revision number of both the item and the item's parent.
+        * @param wrapDoc
+        * @throws Exception
+        */
+       protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+               DocumentModel documentModel = wrapDoc.getWrappedObject();
+               Long rev = (Long)documentModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REV);
+               if (rev == null) {
+                       rev = (long)0;
+               } else {
+                       rev++;
+               }
+               documentModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.REV, rev);
+               //
+               // Next, update the inAuthority (the parent's) rev number
+               //
+               String inAuthorityCsid = this.getInAuthorityCsid();
+               if (inAuthorityCsid == null) {
+                       // When inAuthorityCsid is null, it usually means we're performing and update or synch with the SAS
+                       inAuthorityCsid = (String)documentModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
+               }
+               DocumentModel inAuthorityDocModel = NuxeoUtils.getDocFromCsid(getServiceContext(), getRepositorySession(), inAuthorityCsid);
+               if (inAuthorityDocModel != null) {
+                       Long parentRev = (Long)inAuthorityDocModel.getProperty(getParentCommonSchemaName(), AuthorityJAXBSchema.REV);
+                       if (parentRev == null) {
+                               parentRev = new Long(0);
+                       }
+                          parentRev++;
+                          inAuthorityDocModel.setProperty(getParentCommonSchemaName(), AuthorityJAXBSchema.REV, parentRev);
+                          getRepositorySession().saveDocument(inAuthorityDocModel);
+               } else {
+                       logger.warn(String.format("Containing authority '%s' for item '%s' has been deleted.  Item is orphaned, so revision numbers can't be updated.",
+                                       inAuthorityCsid, documentModel.getName()));
+               }
+       }
+
+       /**
+        * If no short identifier was provided in the input payload, generate a
+        * short identifier from the preferred term display name or term name.
+        */
+       private String handleDisplayNameAsShortIdentifier(DocumentModel docModel) throws Exception {
+               String result = (String) docModel.getProperty(authorityItemCommonSchemaName,
+                               AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
+
+               if (Tools.isEmpty(result)) {
+                       String termDisplayName = getPrimaryDisplayName(
+                                       docModel, authorityItemCommonSchemaName,
+                                       getItemTermInfoGroupXPathBase(),
+                                       AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
+
+                       String termName = getPrimaryDisplayName(
+                                       docModel, authorityItemCommonSchemaName,
+                                       getItemTermInfoGroupXPathBase(),
+                                       AuthorityItemJAXBSchema.TERM_NAME);
+
+                       String generatedShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(termDisplayName,
+                                                       termName);
+                       docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER,
+                                       generatedShortIdentifier);
+                       result = generatedShortIdentifier;
+               }
+
+               return result;
+       }
+
+       /**
+        * Generate a refName for the authority item from the short identifier
+        * and display name.
+        *
+        * All refNames for authority items are generated.  If a client supplies
+        * a refName, it will be overwritten during create (per this method)
+        * or discarded during update (per filterReadOnlyPropertiesForPart).
+        *
+        * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
+        *
+        */
+       protected String updateRefnameForAuthorityItem(DocumentModel docModel,
+                       String schemaName) throws Exception {
+               String result = null;
+
+               RefName.RefNameInterface refname = getRefName(getServiceContext(), docModel);
+               String refNameStr = refname.toString();
+               docModel.setProperty(schemaName, AuthorityItemJAXBSchema.REF_NAME, refNameStr);
+               result = refNameStr;
+
+               return result;
+       }
+
+       /**
+        * Check the logic around the parent pointer. Note that we only need do this on
+        * create, since we have logic to make this read-only on update.
+        *
+        * @param docModel
+        *
+        * @throws Exception the exception
+        */
+       private void handleInAuthority(DocumentModel docModel) throws Exception {
+               if (inAuthority == null) { // Only happens on queries to wildcarded authorities
+                       throw new IllegalStateException("Trying to Create an object with no inAuthority value!");
+               }
+               docModel.setProperty(authorityItemCommonSchemaName, 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,
+                       List<String> serviceTypes,
+                       String propertyName,
+                       String itemcsid,
+                       Integer pageNum,
+                       Integer pageSize,
+                       boolean useDefaultOrderByClause,
+                       boolean computeTotal) throws Exception {
+               AuthorityRefDocList authRefDocList = null;
+               CoreSessionInterface repoSession = (CoreSessionInterface) ctx.getCurrentRepositorySession();
+               boolean releaseRepoSession = false;
+
+               try {
+                       NuxeoRepositoryClientImpl repoClient = (NuxeoRepositoryClientImpl)this.getRepositoryClient(ctx);
+                       repoSession = this.getRepositorySession();
+                       if (repoSession == null) {
+                               repoSession = repoClient.getRepositorySession(ctx);
+                               releaseRepoSession = true;
+                       }
+                       DocumentFilter myFilter = getDocumentFilter();
+                       if (pageSize != null) {
+                               myFilter.setPageSize(pageSize);
+                       }
+                       if (pageNum != null) {
+                               myFilter.setStartPage(pageNum);
+                       }
+                       myFilter.setUseDefaultOrderByClause(useDefaultOrderByClause);
+
+                       try {
+                               DocumentWrapper<DocumentModel> wrapper = repoClient.getDoc(repoSession, ctx, itemcsid);
+                               DocumentModel docModel = wrapper.getWrappedObject();
+                               String refName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityItemJAXBSchema.REF_NAME); //docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
+                               authRefDocList = RefNameServiceUtils.getAuthorityRefDocs(
+                                               repoSession,
+                                               ctx,
+                                               repoClient,
+                                               serviceTypes,
+                                               refName,
+                                               propertyName,
+                                               myFilter,
+                                               useDefaultOrderByClause,
+                                               computeTotal /*computeTotal*/);
+                       } catch (PropertyException pe) {
+                               throw pe;
+                       } catch (DocumentException de) {
+                               throw de;
+                       } catch (Exception e) {
+                               if (logger.isDebugEnabled()) {
+                                       logger.debug("Caught exception ", e);
+                               }
+                               throw new DocumentException(e);
+                       } finally {
+                               // If we got/aquired a new seesion then we're responsible for releasing it.
+                               if (releaseRepoSession && repoSession != null) {
+                                       repoClient.releaseRepositorySession(ctx, repoSession);
+                               }
+                       }
+               } catch (Exception e) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("Caught exception ", e);
+                       }
+                       throw new DocumentException(e);
+               }
+
+               return authRefDocList;
+       }
+
+       /* (non-Javadoc)
+        * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
+        */
+       @Override
+       protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
+                       throws Exception {
+               Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
+
+               // Add the CSID to the common part, since they may have fetched via the shortId.
+               if (partMeta.getLabel().equalsIgnoreCase(authorityItemCommonSchemaName)) {
+                       String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
+                       unQObjectProperties.put("csid", csid);
+               }
+
+               return unQObjectProperties;
+       }
+
+       /**
+        * Filters out selected values supplied in an update request.
+        *
+        * For example, filters out AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure
+        * that the link to the item's parent remains untouched.
+        *
+        * @param objectProps the properties filtered out from the update payload
+        * @param partMeta metadata for the object to fill
+        */
+       @Override
+       public void filterReadOnlyPropertiesForPart(
+                       Map<String, Object> objectProps, ObjectPartType partMeta) {
+               super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
+               String commonPartLabel = getServiceContext().getCommonPartLabel();
+               if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
+                       objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
+                       objectProps.remove(AuthorityItemJAXBSchema.CSID);
+                       if (getServiceContext().shouldForceUpdateRefnameReferences() == false) {
+                               objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
+                       }
+                       objectProps.remove(AuthorityItemJAXBSchema.REF_NAME);
+               }
+       }
+
+       /**
+        * Returns the items in a list of term display names whose names contain
+        * a partial term (as might be submitted in a search query, for instance).
+        * @param termDisplayNameList a list of term display names.
+        * @param partialTerm a partial term display name; that is, a portion
+        * of a display name that might be expected to match 0-n terms in the list.
+        * @return a list of term display names that matches the partial term.
+        * Matches are case-insensitive. As well, before matching is performed, any
+        * special-purpose characters that may appear in the partial term (such as
+        * wildcards and anchor characters) are filtered out from both compared terms.
+        */
+       protected List<String> getPartialTermDisplayNameMatches(List<String> termDisplayNameList, String partialTerm) {
+               List<String> result = new ArrayList<>();
+               String partialTermMatchExpression = filterAnchorAndWildcardChars(partialTerm).toLowerCase();
+               try {
+                       for (String termDisplayName : termDisplayNameList) {
+                               if (termDisplayName.toLowerCase()
+                                               .matches(partialTermMatchExpression) == true) {
+                                               result.add(termDisplayName);
+                               }
+                       }
+               } catch (PatternSyntaxException pse) {
+                       logger.warn("Error in regex match pattern '%s' for term display names: %s",
+                                       partialTermMatchExpression, pse.getMessage());
+               }
+               return result;
+       }
+
+       /**
+        * Filters user-supplied anchor and wildcard characters in a string,
+        * replacing them with equivalent regular expressions.
+        * @param term a term in which to filter anchor and wildcard characters.
+        * @return the term with those characters filtered.
+        */
+       protected String filterAnchorAndWildcardChars(String term) {
+               if (Tools.isBlank(term)) {
+                       return term;
+               }
+               if (term.length() < 3) {
+                       return term;
+               }
+               if (logger.isTraceEnabled()) {
+                       logger.trace(String.format("Term = %s", term));
+               }
+               Boolean anchorAtStart = false;
+               Boolean anchorAtEnd = false;
+               String filteredTerm;
+               StringBuilder filteredTermBuilder = new StringBuilder(term);
+               // Term contains no anchor or wildcard characters.
+               if ( (! term.contains(NuxeoRepositoryClientImpl.USER_SUPPLIED_ANCHOR_CHAR))
+                               && (! term.contains(NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD)) ) {
+                       filteredTerm = term;
+               } else {
+                       // Term contains at least one such character.
+                       try {
+                               // Filter the starting anchor or wildcard character, if any.
+                               String firstChar = filteredTermBuilder.substring(0,1);
+                               switch (firstChar) {
+                                       case NuxeoRepositoryClientImpl.USER_SUPPLIED_ANCHOR_CHAR:
+                                               anchorAtStart = true;
+                                               break;
+                                       case NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD:
+                                               filteredTermBuilder.deleteCharAt(0);
+                                               break;
+                               }
+                               if (logger.isTraceEnabled()) {
+                                       logger.trace(String.format("After first char filtering = %s", filteredTermBuilder.toString()));
+                               }
+                               // Filter the ending anchor or wildcard character, if any.
+                               int lastPos = filteredTermBuilder.length() - 1;
+                               String lastChar = filteredTermBuilder.substring(lastPos);
+                               switch (lastChar) {
+                                       case NuxeoRepositoryClientImpl.USER_SUPPLIED_ANCHOR_CHAR:
+                                               filteredTermBuilder.deleteCharAt(lastPos);
+                                               filteredTermBuilder.insert(filteredTermBuilder.length(), NuxeoRepositoryClientImpl.ENDING_ANCHOR_CHAR);
+                                               anchorAtEnd = true;
+                                               break;
+                                       case NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD:
+                                               filteredTermBuilder.deleteCharAt(lastPos);
+                                               break;
+                               }
+                               if (logger.isTraceEnabled()) {
+                                       logger.trace(String.format("After last char filtering = %s", filteredTermBuilder.toString()));
+                               }
+                               filteredTerm = filteredTermBuilder.toString();
+                               // Filter all other wildcards, if any.
+                               filteredTerm = filteredTerm.replaceAll(NuxeoRepositoryClientImpl.USER_SUPPLIED_WILDCARD_REGEX, ZERO_OR_MORE_ANY_CHAR_REGEX);
+                               if (logger.isTraceEnabled()) {
+                                       logger.trace(String.format("After replacing user wildcards = %s", filteredTerm));
+                               }
+                       } catch (Exception e) {
+                               logger.warn(String.format("Error filtering anchor and wildcard characters from string: %s", e.getMessage()));
+                               return term;
+                       }
+               }
+               // Wrap the term in beginning and ending regex wildcards, unless a
+               // starting or ending anchor character was present.
+               return (anchorAtStart ? "" : ZERO_OR_MORE_ANY_CHAR_REGEX)
+                               + filteredTerm
+                               + (anchorAtEnd ? "" : ZERO_OR_MORE_ANY_CHAR_REGEX);
+       }
+
+       @SuppressWarnings("unchecked")
+       private List<String> getPartialTermDisplayNameMatches(DocumentModel docModel, // REM - CSPACE-5133
+                       String schema, ListResultField field, String partialTerm) {
+               List<String> result = null;
+
+               String xpath = field.getXpath(); // results in something like "persons_common:personTermGroupList/[0]/termDisplayName"
+               int endOfTermGroup = xpath.lastIndexOf("/[0]/");
+               String propertyName = endOfTermGroup != -1 ? xpath.substring(0, endOfTermGroup) : xpath; // it may not be multivalued so the xpath passed in would be the property name
+               Object value = null;
+
+               try {
+                       value = docModel.getProperty(schema, propertyName);
+               } catch (Exception e) {
+                       logger.error("Could not extract term display name with property = "
+                                       + propertyName, e);
+               }
+
+               if (value != null && value instanceof ArrayList) {
+                       ArrayList<HashMap<String, Object>> termGroupList = (ArrayList<HashMap<String, Object>>)value;
+                       int arrayListSize = termGroupList.size();
+                       if (arrayListSize > 1) { // if there's only 1 element in the list then we've already matched the primary term's display name
+                               List<String> displayNameList = new ArrayList<String>();
+                               for (int i = 1; i < arrayListSize; i++) { // start at 1, skip the primary term's displayName since we will always return it
+                                       HashMap<String, Object> map = (HashMap<String, Object>)termGroupList.get(i);
+                                       String termDisplayName = (String) map.get(AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
+                                       displayNameList.add(i - 1, termDisplayName);
+                               }
+
+                               result = getPartialTermDisplayNameMatches(displayNameList, partialTerm);
+                       }
+               }
+
+               return result;
+       }
+
+       private boolean isTermReferenced(DocumentModel docModel) throws Exception {
+               boolean result = false;
+
+               AuthorityRefDocList referenceList = null;
+
+               String wf_deletedStr = (String) getServiceContext().getQueryParams().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+               if (wf_deletedStr != null && Tools.isFalse(wf_deletedStr)) {
+                       //
+                       // if query param 'wf_deleted=false', we won't count references to soft-deleted records
+                       //
+                       referenceList = getReferencingObjectsForMarkingTerm(getServiceContext(), docModel, RefObjsSearchType.NON_DELETED);
+               } else {
+                       //
+                       // if query param 'wf_deleted=true' or missing, we count references to soft-deleted and active records
+                       //
+                       referenceList = getReferencingObjectsForMarkingTerm(getServiceContext(), docModel, RefObjsSearchType.ALL);
+               }
+
+               if (referenceList.getTotalItems() > 0) {
+                       result = true;
+               }
+
+               return result;
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
+                       String schema, ListResultField field) throws DocumentException {
+               Object result = null;
+               String fieldXPath = field.getXpath();
+
+               if (fieldXPath.equalsIgnoreCase(AuthorityClient.REFERENCED) == false) {
+                       result = NuxeoUtils.getXPathValue(docModel, schema, field.getXpath());
+               } else {
+                       //
+                       // Check to see if the request is asking us to mark terms as referenced or not.
+                       //
+                       String markIfReferencedStr = (String) getServiceContext().getQueryParams().getFirst(AuthorityClient.MARK_IF_REFERENCED_QP);
+                       if (Tools.isTrue(markIfReferencedStr) == false) {
+                               return null; // leave the <referenced> element as null since they're not asking for it
+                       } else try {
+                               return Boolean.toString(isTermReferenced(docModel)); // set the <referenced> element to either 'true' or 'false'
+                       } catch (Exception e) {
+                               String msg = String.format("Failed while trying to find records referencing term CSID='%s'.", docModel.getName());
+                               throw new DocumentException(msg, e);
+                       }
+               }
+
+               //
+               // Special handling of list item values for authority items (only)
+               // takes place here:
+               //
+               // If the list result field is the termDisplayName element,
+               // check whether a partial term matching query was made.
+               // If it was, emit values for both the preferred (aka primary)
+               // term and for all non-preferred terms, if any.
+               //
+               String elName = field.getElement();
+               if (isTermDisplayName(elName) == true) {
+                       MultivaluedMap<String, String> queryParams = this.getServiceContext().getQueryParams();
+                       String partialTerm = queryParams != null ? queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM) : null;
+                       if (partialTerm != null && partialTerm.trim().isEmpty() == false) {
+                               String primaryTermDisplayName = (String)result;
+                               List<String> matches = getPartialTermDisplayNameMatches(docModel, schema, field, partialTerm);
+                               if (matches != null && matches.isEmpty() == false) {
+                                       matches.add(0, primaryTermDisplayName); // insert the primary term's display name at the beginning of the list
+                                       result = matches; // set the result to a list of matching term display names with the primary term's display name at the beginning
+                               }
+                       }
+               }
+
+               return result;
+       }
+
+       @Override
+       public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+               MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
+               super.extractAllParts(wrapDoc);
+       }
+
+       protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList) {
+               List<RelationsCommonList.RelationListItem> result = newRelationsCommonList();
+               for (RelationsCommonList.RelationListItem item : inboundList) {
+                       result.add(item);
+               }
+               return result;
+       }
+
+
+       /* don't even THINK of re-using this method.
+        * String example_uri = "/locationauthorities/7ec60f01-84ab-4908-9a6a/items/a5466530-713f-43b4-bc05";
+        */
+       @Deprecated
+       private String extractInAuthorityCSID(String uri) {
+               String IN_AUTHORITY_REGEX = "/(.*?)/(.*?)/(.*)";
+               Pattern p = Pattern.compile(IN_AUTHORITY_REGEX);
+               Matcher m = p.matcher(uri);
+               if (m.find()) {
+                       if (m.groupCount() < 3) {
+                               logger.warn("REGEX-WRONG-GROUPCOUNT looking in " + uri);
+                               return "";
+                       } else {
+                               //String service = m.group(1);
+                               String inauth = m.group(2);
+                               //String theRest = m.group(3);
+                               return inauth;
+                               //print("service:"+service+", inauth:"+inauth+", rest:"+rest);
+                       }
+               } else {
+                       logger.warn("REGEX-NOT-MATCHED looking in " + uri);
+                       return "";
+               }
+       }
+
+       //ensures CSPACE-4042
+       protected void uriPointsToSameAuthority(String thisURI, String inboundItemURI) throws Exception {
+               String authorityCSID = extractInAuthorityCSID(thisURI);
+               String authorityCSIDForInbound = extractInAuthorityCSID(inboundItemURI);
+               if (Tools.isBlank(authorityCSID)
+                               || Tools.isBlank(authorityCSIDForInbound)
+                               || (!authorityCSID.equalsIgnoreCase(authorityCSIDForInbound))) {
+                       throw new Exception("Item URI " + thisURI + " must point to same authority as related item: " + inboundItemURI);
+               }
+       }
+
+       public String getItemTermInfoGroupXPathBase() {
+               return authorityItemTermGroupXPathBase;
+       }
+
+       public void setItemTermInfoGroupXPathBase(String itemTermInfoGroupXPathBase) {
+               authorityItemTermGroupXPathBase = itemTermInfoGroupXPathBase;
+       }
+
+       protected String getAuthorityItemCommonSchemaName() {
+               return authorityItemCommonSchemaName;
+       }
+
+       // @Override
+       public boolean isJDBCQuery() {
+               boolean result = false;
+
+               MultivaluedMap<String, String> queryParams = getServiceContext().getQueryParams();
+               //
+               // Look the query params to see if we need to make a SQL query.
+               //
+               String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
+               if (partialTerm != null && partialTerm.trim().isEmpty() == false) {
+                       result = true;
+               }
+
+               return result;
+       }
+
+       // By convention, the name of the database table that contains
+       // repeatable term information group records is derivable from
+       // an existing XPath base value, by removing a suffix and converting
+       // to lowercase
+       protected String getTermGroupTableName() {
+               String termInfoGroupListName = getItemTermInfoGroupXPathBase();
+               return termInfoGroupListName.substring(0, termInfoGroupListName.lastIndexOf(LIST_SUFFIX)).toLowerCase();
+       }
+
+       protected String getInAuthorityValue() {
+               String inAuthorityValue = getInAuthorityCsid();
+               if (Tools.notBlank(inAuthorityValue)) {
+                       return inAuthorityValue;
+               } else {
+                       return AuthorityResource.PARENT_WILDCARD;
+               }
+       }
+
+       @Override
+       public Map<String,String> getJDBCQueryParams() {
+               // FIXME: Get all of the following values from appropriate external constants.
+               // At present, these are duplicated in both RepositoryClientImpl
+               // and in AuthorityItemDocumentModelHandler.
+               final String TERM_GROUP_LIST_NAME = "TERM_GROUP_LIST_NAME";
+               final String TERM_GROUP_TABLE_NAME_PARAM = "TERM_GROUP_TABLE_NAME";
+               final String IN_AUTHORITY_PARAM = "IN_AUTHORITY";
+
+               Map<String,String> params = super.getJDBCQueryParams();
+               params.put(TERM_GROUP_LIST_NAME, getItemTermInfoGroupXPathBase());
+               params.put(TERM_GROUP_TABLE_NAME_PARAM, getTermGroupTableName());
+               params.put(IN_AUTHORITY_PARAM, getInAuthorityValue());
+               return params;
+       }
+
 }