]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-3779: Adding new authority module.
authorRichard Millet <richard.millet@berkeley.edu>
Fri, 8 Apr 2011 20:12:38 +0000 (20:12 +0000)
committerRichard Millet <richard.millet@berkeley.edu>
Fri, 8 Apr 2011 20:12:38 +0000 (20:12 +0000)
12 files changed:
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityItemJAXBSchema.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityItemListItemJAXBSchema.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityJAXBSchema.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityListItemJAXBSchema.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/IVocabManager.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/RefNameUtils.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/VocabManager.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/VocabManagerImpl.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java [new file with mode: 0644]
services/authority/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java [new file with mode: 0644]

diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityItemJAXBSchema.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityItemJAXBSchema.java
new file mode 100644 (file)
index 0000000..b6be3b3
--- /dev/null
@@ -0,0 +1,42 @@
+/**\r
+ *  This document is a part of the source code and related artifacts\r
+ *  for CollectionSpace, an open source collections management system\r
+ *  for museums and related institutions:\r
+\r
+ *  http://www.collectionspace.org\r
+ *  http://wiki.collectionspace.org\r
+\r
+ *  Copyright 2009 University of California at Berkeley\r
+\r
+ *  Licensed under the Educational Community License (ECL), Version 2.0.\r
+ *  You may not use this file except in compliance with this License.\r
+\r
+ *  You may obtain a copy of the ECL 2.0 License at\r
+\r
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt\r
+\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS,\r
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ */\r
+package org.collectionspace.services.common.vocabulary;\r
+\r
+/**\r
+ * @author pschmitz\r
+ *\r
+ */\r
+public interface AuthorityItemJAXBSchema {\r
+       final static String DISPLAY_NAME = "displayName";\r
+       final static String DISPLAY_NAME_COMPUTED = "displayNameComputed";\r
+       final static String SHORT_DISPLAY_NAME = "shortDisplayName";\r
+       final static String SHORT_DISPLAY_NAME_COMPUTED = "shortDisplayNameComputed";\r
+       final static String IN_AUTHORITY = "inAuthority";\r
+       final static String REF_NAME = "refName";\r
+       final static String SHORT_IDENTIFIER = "shortIdentifier";\r
+       final static String TERM_STATUS = "termStatus";\r
+       final static String CSID = "csid";\r
+}\r
+\r
+\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityItemListItemJAXBSchema.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityItemListItemJAXBSchema.java
new file mode 100644 (file)
index 0000000..b5d2f0d
--- /dev/null
@@ -0,0 +1,33 @@
+/**\r
+ *  This document is a part of the source code and related artifacts\r
+ *  for CollectionSpace, an open source collections management system\r
+ *  for museums and related institutions:\r
+\r
+ *  http://www.collectionspace.org\r
+ *  http://wiki.collectionspace.org\r
+\r
+ *  Copyright 2009 University of California at Berkeley\r
+\r
+ *  Licensed under the Educational Community License (ECL), Version 2.0.\r
+ *  You may not use this file except in compliance with this License.\r
+\r
+ *  You may obtain a copy of the ECL 2.0 License at\r
+\r
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt\r
+\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS,\r
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ */\r
+package org.collectionspace.services.common.vocabulary;\r
+\r
+public interface AuthorityItemListItemJAXBSchema {\r
+       final static String DISPLAY_NAME = "displayName";\r
+       final static String SHORT_DISPLAY_NAME = "shortDisplayName";\r
+       final static String REF_NAME = "refName";\r
+       final static String SHORT_IDENTIFIER = "shortIdentifier";\r
+       final static String CSID = "csid";\r
+       final static String URI = "url";\r
+}\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityJAXBSchema.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityJAXBSchema.java
new file mode 100644 (file)
index 0000000..456ea65
--- /dev/null
@@ -0,0 +1,38 @@
+/**\r
+ *  This document is a part of the source code and related artifacts\r
+ *  for CollectionSpace, an open source collections management system\r
+ *  for museums and related institutions:\r
+\r
+ *  http://www.collectionspace.org\r
+ *  http://wiki.collectionspace.org\r
+\r
+ *  Copyright 2009 University of California at Berkeley\r
+\r
+ *  Licensed under the Educational Community License (ECL), Version 2.0.\r
+ *  You may not use this file except in compliance with this License.\r
+\r
+ *  You may obtain a copy of the ECL 2.0 License at\r
+\r
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt\r
+\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS,\r
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ */\r
+package org.collectionspace.services.common.vocabulary;\r
+\r
+/**\r
+ * @author pschmitz\r
+ *\r
+ */\r
+public interface AuthorityJAXBSchema {\r
+       final static String DISPLAY_NAME = "displayName";\r
+       final static String SHORT_IDENTIFIER = "shortIdentifier";\r
+       final static String REF_NAME = "refName";\r
+       final static String VOCAB_TYPE = "vocabType";\r
+       final static String CSID = "csid";\r
+}\r
+\r
+\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityListItemJAXBSchema.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityListItemJAXBSchema.java
new file mode 100644 (file)
index 0000000..b02c59a
--- /dev/null
@@ -0,0 +1,33 @@
+/**\r
+ *  This document is a part of the source code and related artifacts\r
+ *  for CollectionSpace, an open source collections management system\r
+ *  for museums and related institutions:\r
+\r
+ *  http://www.collectionspace.org\r
+ *  http://wiki.collectionspace.org\r
+\r
+ *  Copyright 2009 University of California at Berkeley\r
+\r
+ *  Licensed under the Educational Community License (ECL), Version 2.0.\r
+ *  You may not use this file except in compliance with this License.\r
+\r
+ *  You may obtain a copy of the ECL 2.0 License at\r
+\r
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt\r
+\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS,\r
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ */\r
+package org.collectionspace.services.common.vocabulary;\r
+\r
+public interface AuthorityListItemJAXBSchema {\r
+       final static String DISPLAY_NAME = "displayName";\r
+       final static String REF_NAME = "refName";\r
+       final static String SHORT_IDENTIFIER = "shortIdentifier";\r
+       final static String VOCAB_TYPE = "vocabType";\r
+       final static String CSID = "csid";\r
+       final static String URI = "url";\r
+}\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java
new file mode 100644 (file)
index 0000000..a5fce2f
--- /dev/null
@@ -0,0 +1,956 @@
+/**
+ *  This document is a part of the source code and related artifacts
+ *  for CollectionSpace, an open source collections management system
+ *  for museums and related institutions:
+
+ *  http://www.collectionspace.org
+ *  http://wiki.collectionspace.org
+
+ *  Copyright 2009 University of California at Berkeley
+
+ *  Licensed under the Educational Community License (ECL), Version 2.0.
+ *  You may not use this file except in compliance with this License.
+
+ *  You may obtain a copy of the ECL 2.0 License at
+
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt
+
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.collectionspace.services.common.vocabulary;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.collectionspace.services.client.IQueryManager;
+import org.collectionspace.services.client.PoxPayloadIn;
+import org.collectionspace.services.client.PoxPayloadOut;
+import org.collectionspace.services.client.workflow.WorkflowClient;
+import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema;
+import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
+import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
+import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
+import org.collectionspace.services.common.AbstractMultiPartCollectionSpaceResourceImpl;
+import org.collectionspace.services.common.ClientType;
+import org.collectionspace.services.common.ServiceMain;
+import org.collectionspace.services.common.ServiceMessages;
+import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
+import org.collectionspace.services.common.authorityref.AuthorityRefList;
+import org.collectionspace.services.common.context.JaxRsContext;
+import org.collectionspace.services.common.context.MultipartServiceContext;
+import org.collectionspace.services.common.context.MultipartServiceContextImpl;
+import org.collectionspace.services.common.context.RemoteServiceContext;
+import org.collectionspace.services.common.context.ServiceBindingUtils;
+import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.BadRequestException;
+import org.collectionspace.services.common.document.DocumentFilter;
+import org.collectionspace.services.common.document.DocumentHandler;
+import org.collectionspace.services.common.document.DocumentNotFoundException;
+import org.collectionspace.services.common.document.DocumentWrapper;
+import org.collectionspace.services.common.repository.RepositoryClient;
+import org.collectionspace.services.common.security.UnauthorizedException;
+import org.collectionspace.services.common.query.QueryManager;
+import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
+import org.jboss.resteasy.util.HttpResponseCodes;
+import org.nuxeo.ecm.core.api.DocumentModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Class AuthorityResource.
+ */
+@Consumes("application/xml")
+@Produces("application/xml")
+public abstract class AuthorityResource<AuthCommon, AuthCommonList, AuthItemCommonList, AuthItemHandler> extends
+       AbstractMultiPartCollectionSpaceResourceImpl {
+
+       protected Class<AuthCommon> authCommonClass;
+       protected Class<?> resourceClass;
+       protected String authorityCommonSchemaName;
+       protected String authorityItemCommonSchemaName;
+
+       final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType();
+       
+       final static String URN_PREFIX = "urn:cspace:";
+       final static int URN_PREFIX_LEN = URN_PREFIX.length();
+       final static String URN_PREFIX_NAME = "name(";
+       final static int URN_NAME_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_NAME.length();
+       final static String URN_PREFIX_ID = "id(";
+       final static int URN_ID_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_ID.length();
+       
+    final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
+    
+    public enum SpecifierForm { CSID, URN_NAME };
+    
+    public class Specifier {
+       public SpecifierForm form;
+       public String value;
+       Specifier(SpecifierForm form, String value) {
+               this.form = form;
+               this.value = value;
+       }
+    }
+    
+    protected Specifier getSpecifier(String specifierIn, String method, String op) throws WebApplicationException {
+               if (logger.isDebugEnabled()) {
+                       logger.debug("getSpecifier called by: "+method+" with specifier: "+specifierIn);
+               }
+               if (specifierIn != null) {
+                       if(!specifierIn.startsWith(URN_PREFIX)) {
+                               // We'll assume it is a CSID and complain if it does not match
+                               return new Specifier(SpecifierForm.CSID, specifierIn);
+                       } else { 
+                               if(specifierIn.startsWith(URN_PREFIX_NAME, URN_PREFIX_LEN)) {
+                                       int closeParen = specifierIn.indexOf(')', URN_NAME_PREFIX_LEN);
+                                       if(closeParen>=0) {
+                                               return new Specifier(SpecifierForm.URN_NAME,
+                                                                       specifierIn.substring(URN_NAME_PREFIX_LEN, closeParen));
+                                       }
+                               } else if(specifierIn.startsWith(URN_PREFIX_ID, URN_PREFIX_LEN)) {
+                                       int closeParen = specifierIn.indexOf(')', URN_ID_PREFIX_LEN);
+                                       if(closeParen>=0) {
+                                               return new Specifier(SpecifierForm.CSID,
+                                                               specifierIn.substring(URN_ID_PREFIX_LEN, closeParen));
+                                       }
+                               }
+                       }
+               }
+               logger.error(method+": bad or missing specifier!");
+               Response response = Response.status(Response.Status.BAD_REQUEST).entity(
+                               op+" failed on bad or missing Authority specifier").type(
+                               "text/plain").build();
+               throw new WebApplicationException(response);
+    }
+
+    /**
+        * Instantiates a new Authority resource.
+        */
+       public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
+                       String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
+               this.authCommonClass = authCommonClass;
+               this.resourceClass = resourceClass;
+               this.authorityCommonSchemaName = authorityCommonSchemaName;
+               this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
+       }
+       
+       public abstract String getItemServiceName();
+
+       /* (non-Javadoc)
+        * @see org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl#getVersionString()
+        */
+       @Override
+       protected String getVersionString() {
+               /** The last change revision. */
+               final String lastChangeRevision = "$LastChangedRevision: 2617 $";
+               return lastChangeRevision;
+       }
+
+       /* (non-Javadoc)
+        * @see org.collectionspace.services.common.CollectionSpaceResource#getCommonPartClass()
+        */
+       @Override
+       public Class<AuthCommon> getCommonPartClass() {
+               return authCommonClass;
+       }
+
+       /**
+        * Creates the item document handler.
+        * 
+        * @param ctx the ctx
+        * @param inAuthority the in vocabulary
+        * 
+        * @return the document handler
+        * 
+        * @throws Exception the exception
+        */
+       public DocumentHandler createItemDocumentHandler(
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
+                       String inAuthority)
+       throws Exception {
+               AuthItemHandler docHandler;
+
+               docHandler = (AuthItemHandler)createDocumentHandler(ctx,
+                               ctx.getCommonPartLabel(getItemServiceName()),
+                               authCommonClass);       
+               ((AuthorityItemDocumentModelHandler<?,?>)docHandler).setInAuthority(inAuthority);
+
+               return (DocumentHandler)docHandler;
+       }
+
+       /**
+        * Creates the authority.
+        * 
+        * @param input the input
+        * 
+        * @return the response
+        */
+       @POST
+       public Response createAuthority(String xmlPayload) {
+               try {
+                       PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
+                       DocumentHandler handler = createDocumentHandler(ctx);
+                       String csid = getRepositoryClient(ctx).create(ctx, handler);
+                       UriBuilder path = UriBuilder.fromResource(resourceClass);
+                       path.path("" + csid);
+                       Response response = Response.created(path.build()).build();
+                       return response;
+               } catch (BadRequestException bre) {
+                       Response response = Response.status(
+                                       Response.Status.BAD_REQUEST).entity("Create failed reason " + bre.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Create failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("Caught exception in createVocabulary", e);
+                       }
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Create failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+       }
+       
+       protected String buildWhereForAuthByName(String name) {
+               return authorityCommonSchemaName+
+                               ":"+AuthorityJAXBSchema.SHORT_IDENTIFIER+
+                               "='"+name+"'";
+       }
+
+       protected String buildWhereForAuthItemByName(String name, String parentcsid) {
+        return
+               authorityItemCommonSchemaName+
+               ":"+AuthorityItemJAXBSchema.SHORT_IDENTIFIER+
+               "='"+name+"' AND "
+                       + authorityItemCommonSchemaName + ":"
+                       + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
+                       + "'" + parentcsid + "'";
+       }
+
+       /**
+        * Gets the authority.
+        * 
+        * @param specifier either a CSID or one of the urn forms
+        * 
+        * @return the authority
+        */
+       @GET
+       @Path("{csid}")
+       public byte[] getAuthority(@PathParam("csid") String specifier) {
+               PoxPayloadOut result = null;
+               try {
+                       Specifier spec = getSpecifier(specifier, "getAuthority", "GET");
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
+                       DocumentHandler handler = createDocumentHandler(ctx);
+                       if(spec.form == SpecifierForm.CSID) {
+                               if (logger.isDebugEnabled()) {
+                                       logger.debug("getAuthority with csid=" + spec.value);
+                               }
+                               getRepositoryClient(ctx).get(ctx, spec.value, handler);
+                       } else {
+                               String whereClause = buildWhereForAuthByName(spec.value);
+                               DocumentFilter myFilter = new DocumentFilter(whereClause, 0, 1);
+                               handler.setDocumentFilter(myFilter);
+                               getRepositoryClient(ctx).get(ctx, handler);
+                       }
+                       result = ctx.getOutput();
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Get failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (DocumentNotFoundException dnfe) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("getAuthority", dnfe);
+                       }
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Get failed on Authority specifier=" + specifier).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("getAuthority", e);
+                       }
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Get failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+
+               if (result == null) {
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Get failed, the requested Authority specifier:" + specifier + ": was not found.").type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+
+               return result.getBytes();
+       }
+
+       /**
+        * Finds and populates the authority list.
+        * 
+        * @param ui the ui
+        * 
+        * @return the authority list
+        */
+    @GET
+    @Produces("application/xml")
+    public AuthCommonList getAuthorityList(@Context UriInfo ui) {
+               try {
+                       MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(queryParams);
+                       DocumentHandler handler = createDocumentHandler(ctx);
+                       DocumentFilter myFilter = handler.getDocumentFilter();
+                       String nameQ = queryParams.getFirst("refName");
+                       if (nameQ != null) {
+                               myFilter.setWhereClause(authorityCommonSchemaName+":refName='" + nameQ + "'");
+                       }
+                       getRepositoryClient(ctx).getFiltered(ctx, handler);
+                       return (AuthCommonList) handler.getCommonPartList();
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Index failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("Caught exception in getAuthorityList", e);
+                       }
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+       }
+
+       /**
+        * Update authority.
+        * 
+        * @param specifier the csid or id
+        * @param theUpdate the the update
+        * 
+        * @return the multipart output
+        */
+       @PUT
+       @Path("{csid}")
+       public byte[] updateAuthority(
+                       @PathParam("csid") String specifier,
+                       String xmlPayload) {
+               PoxPayloadOut result = null;
+               try {
+                       PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
+                       Specifier spec = getSpecifier(specifier, "updateAuthority", "UPDATE");
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
+                       DocumentHandler handler = createDocumentHandler(ctx);
+                       String csid;
+                       if(spec.form==SpecifierForm.CSID) {
+                               csid = spec.value;
+                       } else {
+                               String whereClause = buildWhereForAuthByName(spec.value);
+                               csid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
+                       }
+                       getRepositoryClient(ctx).update(ctx, csid, handler);
+                       result = ctx.getOutput();
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Update failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (DocumentNotFoundException dnfe) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("caught exception in updateAuthority", dnfe);
+                       }
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Update failed on Authority specifier=" + specifier).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Update failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+               return result.getBytes();
+       }
+
+       /**
+        * Delete authority.
+        * 
+        * @param csid the csid
+        * 
+        * @return the response
+        */
+       @DELETE
+       @Path("{csid}")
+       public Response deleteAuthority(@PathParam("csid") String csid) {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug("deleteAuthority with csid=" + csid);
+               }
+               if (csid == null || "".equals(csid)) {
+                       logger.error("deleteAuthority: missing csid!");
+                       Response response = Response.status(Response.Status.BAD_REQUEST).entity(
+                                       "delete failed on Authority csid=" + csid).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+               try {
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
+                       getRepositoryClient(ctx).delete(ctx, csid);
+                       return Response.status(HttpResponseCodes.SC_OK).build();
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Delete failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (DocumentNotFoundException dnfe) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("caught exception in deleteAuthority", dnfe);
+                       }
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Delete failed on Authority csid=" + csid).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Delete failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+
+       }
+
+       /*************************************************************************
+        * Create an AuthorityItem - this is a sub-resource of Authority
+        * @param specifier either a CSID or one of the urn forms
+        * @param input the payload 
+        * @return Authority item response
+        *************************************************************************/
+       @POST
+       @Path("{csid}/items")
+       public Response createAuthorityItem(@PathParam("csid") String specifier, String xmlPayload) {
+               try {
+                       PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
+                       Specifier spec = getSpecifier(specifier, "createAuthorityItem", "CREATE_ITEM");
+                       String parentcsid;
+                       if(spec.form==SpecifierForm.CSID) {
+                               parentcsid = spec.value;
+                       } else {
+                               String whereClause = buildWhereForAuthByName(spec.value);
+                   ctx = createServiceContext(getServiceName());
+                               parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
+                       }
+                       ctx = createServiceContext(getItemServiceName(), input);
+                       DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid);
+                       String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
+                       UriBuilder path = UriBuilder.fromResource(resourceClass);
+                       path.path(parentcsid + "/items/" + itemcsid);
+                       Response response = Response.created(path.build()).build();
+                       return response;
+               } catch (BadRequestException bre) {
+                       Response response = Response.status(
+                                       Response.Status.BAD_REQUEST).entity("Create failed reason " + bre.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Create failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("Caught exception in createAuthorityItem", e);
+                       }
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Create failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+       }
+
+    @GET
+    @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
+    public byte[] getItemWorkflow(
+            @PathParam("csid") String csid,
+            @PathParam("itemcsid") String itemcsid) {
+        PoxPayloadOut result = null;
+
+        try {          
+            ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
+            String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
+               
+               MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
+               WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
+               ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
+            getRepositoryClient(ctx).get(ctx, itemcsid, handler);
+            result = ctx.getOutput();
+        } catch (Exception e) {
+            throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
+        }
+                
+        return result.getBytes();
+    }
+    
+    @PUT
+    @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
+    public byte[] updateWorkflow(
+            @PathParam("csid") String csid,
+            @PathParam("itemcsid") String itemcsid,
+               String xmlPayload) {
+        PoxPayloadOut result = null;
+       try {
+               ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
+               String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
+               
+               PoxPayloadIn workflowUpdate = new PoxPayloadIn(xmlPayload);
+               MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, workflowUpdate);
+               WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
+               ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
+               getRepositoryClient(ctx).update(ctx, itemcsid, handler);
+               result = ctx.getOutput();
+        } catch (Exception e) {
+            throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
+        }
+        return result.getBytes();
+    }    
+    
+       
+       /**
+        * Gets the authority item.
+        * 
+        * @param parentspecifier either a CSID or one of the urn forms
+        * @param itemspecifier either a CSID or one of the urn forms
+        * 
+        * @return the authority item
+        */
+       @GET
+       @Path("{csid}/items/{itemcsid}")
+       public byte[] getAuthorityItem(
+               @Context Request request,
+            @Context UriInfo ui,
+                       @PathParam("csid") String parentspecifier,
+                       @PathParam("itemcsid") String itemspecifier) {
+               PoxPayloadOut result = null;
+               try {
+               JaxRsContext jaxRsContext = new JaxRsContext(request, ui);
+
+                       Specifier parentSpec = getSpecifier(parentspecifier, "getAuthorityItem(parent)", "GET_ITEM");
+                       Specifier itemSpec = getSpecifier(itemspecifier, "getAuthorityItem(item)", "GET_ITEM");
+                       // Note that we have to create the service context for the Items, not the main service
+                       RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
+                       String parentcsid;
+                       if(parentSpec.form==SpecifierForm.CSID) {
+                               parentcsid = parentSpec.value;
+                       } else {
+                               String whereClause = buildWhereForAuthByName(parentSpec.value);
+                               ctx = (RemoteServiceContext)createServiceContext(getServiceName());
+                               parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
+                       }
+                       ctx = (RemoteServiceContext)createServiceContext(getItemServiceName());
+                       ctx.setJaxRsContext(jaxRsContext);
+                       DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid);
+                       if(itemSpec.form==SpecifierForm.CSID) {
+                               getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
+                       } else {
+                               String itemWhereClause = 
+                                       buildWhereForAuthItemByName(itemSpec.value, parentcsid);
+                   DocumentFilter myFilter = new DocumentFilter(itemWhereClause, 0, 1);
+                   handler.setDocumentFilter(myFilter);
+                   getRepositoryClient(ctx).get(ctx, handler);
+                       }
+                       // TODO should we assert that the item is in the passed vocab?
+                       result = ctx.getOutput();
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Get failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (DocumentNotFoundException dnfe) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("getAuthorityItem", dnfe);
+                       }
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Get failed on AuthorityItem specifier=" + itemspecifier).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("getAuthorityItem", e);
+                       }
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Get failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+               if (result == null) {
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Get failed, the requested AuthorityItem specifier:" + itemspecifier + ": was not found.").type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+               return result.getBytes();
+       }
+
+
+       /**
+        * Gets the authorityItem list for the specified authority
+        * If partialPerm is specified, keywords will be ignored.
+        * 
+        * @param specifier either a CSID or one of the urn forms
+        * @param partialTerm if non-null, matches partial terms
+        * @param keywords if non-null, matches terms in the keyword index for items
+        * @param ui passed to include additional parameters, like pagination controls
+        * 
+        * @return the authorityItem list
+        */
+       @GET
+       @Path("{csid}/items")
+       @Produces("application/xml")
+       public AuthItemCommonList getAuthorityItemList(
+                       @PathParam("csid") String specifier,
+                       @QueryParam(IQueryManager.SEARCH_TYPE_PARTIALTERM) String partialTerm,
+                       @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords,
+                       @Context UriInfo ui) {
+               try {
+                       Specifier spec = getSpecifier(specifier, "getAuthorityItemList", "LIST");
+                       MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
+                       // Note that docType defaults to the ServiceName, so we're fine with that.
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
+                       String parentcsid;
+                       if(spec.form==SpecifierForm.CSID) {
+                               parentcsid = spec.value;
+                       } else {
+                               String whereClause = buildWhereForAuthByName(spec.value);
+                               ctx = createServiceContext(getServiceName());
+                               parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
+                       }
+                       ctx = createServiceContext(getItemServiceName(), queryParams);
+                       DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid);
+                       DocumentFilter myFilter = handler.getDocumentFilter();
+                       myFilter.setWhereClause(
+                                       authorityItemCommonSchemaName + ":"
+                                       + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
+                                       + "'" + parentcsid + "'");
+
+                       // AND vocabularyitems_common:displayName LIKE '%partialTerm%'
+                       if (partialTerm != null && !partialTerm.isEmpty()) {
+                               String ptClause = QueryManager.createWhereClauseForPartialMatch(
+                               authorityItemCommonSchemaName + ":"
+                               + AuthorityItemJAXBSchema.DISPLAY_NAME, partialTerm );
+                               myFilter.appendWhereClause(ptClause, IQueryManager.SEARCH_QUALIFIER_AND);
+                       } else if (keywords != null) {
+                               String kwdClause = QueryManager.createWhereClauseFromKeywords(keywords);
+                               myFilter.appendWhereClause(kwdClause, IQueryManager.SEARCH_QUALIFIER_AND);
+                       }
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("getAuthorityItemList filtered WHERE clause: "
+                                               + myFilter.getWhereClause());
+                       }
+                       getRepositoryClient(ctx).getFiltered(ctx, handler);
+                       return (AuthItemCommonList) handler.getCommonPartList();
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Index failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("Caught exception in getAuthorityItemList", e);
+                       }
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+       }
+
+    /**
+     * Gets the entities referencing this Authority item instance. The service type
+     * can be passed as a query param "type", and must match a configured type
+     * for the service bindings. If not set, the type defaults to
+     * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
+     *
+        * @param parentspecifier either a CSID or one of the urn forms
+        * @param itemspecifier either a CSID or one of the urn forms
+     * @param ui the ui
+     * 
+     * @return the info for the referencing objects
+     */
+    @GET
+    @Path("{csid}/items/{itemcsid}/refObjs")
+    @Produces("application/xml")
+    public AuthorityRefDocList getReferencingObjects(
+                       @PathParam("csid") String parentspecifier,
+                       @PathParam("itemcsid") String itemspecifier,
+               @Context UriInfo ui) {
+       AuthorityRefDocList authRefDocList = null;
+       try {
+               MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
+                       Specifier parentSpec = getSpecifier(parentspecifier, 
+                                       "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS");
+                       Specifier itemSpec = getSpecifier(itemspecifier, 
+                                       "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
+                       // Note that we have to create the service context for the Items, not the main service
+            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
+                       String parentcsid;
+                       if(parentSpec.form==SpecifierForm.CSID) {
+                               parentcsid = parentSpec.value;
+                       } else {
+                               String whereClause = buildWhereForAuthByName(parentSpec.value);
+                   ctx = createServiceContext(getServiceName());
+                               parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
+                       }
+               ctx = createServiceContext(getItemServiceName(), queryParams);
+            String itemcsid;
+                       if(itemSpec.form==SpecifierForm.CSID) {
+                               itemcsid = itemSpec.value;
+                       } else {
+                               String itemWhereClause = 
+                                       buildWhereForAuthItemByName(itemSpec.value, parentcsid);
+                               itemcsid = getRepositoryClient(ctx).findDocCSID(ctx, itemWhereClause);
+                       }
+               // Note that we have to create the service context for the Items, not the main service
+               DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid);
+               RepositoryClient repoClient = getRepositoryClient(ctx); 
+               DocumentFilter myFilter = handler.getDocumentFilter();
+               String serviceType = ServiceBindingUtils.SERVICE_TYPE_PROCEDURE;
+               List<String> list = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
+               if (list != null) {
+                       serviceType = list.get(0);
+               }
+               DocumentWrapper<DocumentModel> docWrapper = repoClient.getDoc(ctx, itemcsid);
+               DocumentModel docModel = docWrapper.getWrappedObject();
+               String refName = (String)docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
+
+               authRefDocList = RefNameServiceUtils.getAuthorityRefDocs(ctx,
+                               repoClient, 
+                               serviceType,
+                               refName,
+                               myFilter.getPageSize(), myFilter.getStartPage(), true /*computeTotal*/ );
+       } catch (UnauthorizedException ue) {
+               Response response = Response.status(
+                               Response.Status.UNAUTHORIZED).entity("Get failed reason " + ue.getErrorReason()).type("text/plain").build();
+               throw new WebApplicationException(response);
+       } catch (DocumentNotFoundException dnfe) {
+               if (logger.isDebugEnabled()) {
+                       logger.debug("getReferencingObjects", dnfe);
+               }
+               Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                               "GetReferencingObjects failed with parentspecifier=" 
+                               + parentspecifier + " and itemspecifier=" + itemspecifier).type(
+                               "text/plain").build();
+               throw new WebApplicationException(response);
+       } catch (Exception e) { // Includes DocumentException
+               if (logger.isDebugEnabled()) {
+                       logger.debug("GetReferencingObjects", e);
+               }
+               Response response = Response.status(
+                               Response.Status.INTERNAL_SERVER_ERROR).entity("Get failed").type("text/plain").build();
+               throw new WebApplicationException(response);
+       }
+       if (authRefDocList == null) {
+               Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                               "Get failed, the requested Item CSID:" + itemspecifier + ": was not found.").type(
+                               "text/plain").build();
+               throw new WebApplicationException(response);
+       }
+       return authRefDocList;
+    }
+
+    /**
+     * Gets the authority terms used in the indicated Authority item.
+     *
+        * @param parentspecifier either a CSID or one of the urn forms
+        * @param itemspecifier either a CSID or one of the urn forms
+        * @param ui passed to include additional parameters, like pagination controls
+     *
+     * @return the authority refs for the Authority item.
+     */
+    @GET
+    @Path("{csid}/items/{itemcsid}/authorityrefs")
+    @Produces("application/xml")
+    public AuthorityRefList getAuthorityItemAuthorityRefs(
+               @PathParam("csid") String parentspecifier,
+               @PathParam("itemcsid") String itemspecifier,
+               @Context UriInfo ui) {
+       AuthorityRefList authRefList = null;
+        try {
+                       Specifier parentSpec = getSpecifier(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS");
+                       Specifier itemSpec = getSpecifier(itemspecifier, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
+                       // Note that we have to create the service context for the Items, not the main service
+            MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
+            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
+                       String parentcsid;
+                       if(parentSpec.form==SpecifierForm.CSID) {
+                               parentcsid = parentSpec.value;
+                       } else {
+                               String whereClause = buildWhereForAuthByName(parentSpec.value);
+                   ctx = createServiceContext(getServiceName());
+                               parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
+                       }
+            ctx = createServiceContext(getItemServiceName(), queryParams);
+            RemoteDocumentModelHandlerImpl handler =
+                (RemoteDocumentModelHandlerImpl) createItemDocumentHandler(ctx, parentcsid);
+            String itemcsid;
+                       if(itemSpec.form==SpecifierForm.CSID) {
+                               itemcsid = itemSpec.value;
+                       } else {
+                               String itemWhereClause = 
+                                       buildWhereForAuthItemByName(itemSpec.value, parentcsid);
+                               itemcsid = getRepositoryClient(ctx).findDocCSID(ctx, itemWhereClause);
+                       }
+            DocumentWrapper<DocumentModel> docWrapper =
+               getRepositoryClient(ctx).getDoc(ctx, itemcsid);
+            List<String> authRefFields =
+               ((MultipartServiceContextImpl)ctx).getCommonPartPropertyValues(
+               ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
+            authRefList = handler.getAuthorityRefs(docWrapper, authRefFields);
+        } catch (UnauthorizedException ue) {
+            Response response = Response.status(
+                    Response.Status.UNAUTHORIZED).entity("Failed to retrieve authority references: reason " + ue.getErrorReason()).type("text/plain").build();
+            throw new WebApplicationException(response);
+        } catch (Exception e) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Caught exception in getAuthorityRefs", e);
+            }
+            Response response = Response.status(
+                    Response.Status.INTERNAL_SERVER_ERROR).entity("Failed to retrieve authority references").type("text/plain").build();
+            throw new WebApplicationException(response);
+        }
+        return authRefList;
+    }
+
+       /**
+        * Update authorityItem.
+        * 
+        * @param parentspecifier either a CSID or one of the urn forms
+        * @param itemspecifier either a CSID or one of the urn forms
+        * @param theUpdate the the update
+        * 
+        * @return the multipart output
+        */
+       @PUT
+       @Path("{csid}/items/{itemcsid}")
+       public byte[] updateAuthorityItem(
+                       @PathParam("csid") String parentspecifier,
+                       @PathParam("itemcsid") String itemspecifier,
+                       String xmlPayload) {
+               PoxPayloadOut result = null;
+               try {
+                       PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
+                       Specifier parentSpec = getSpecifier(parentspecifier, 
+                                       "updateAuthorityItem(parent)", "UPDATE_ITEM");
+                       Specifier itemSpec = getSpecifier(itemspecifier, 
+                                       "updateAuthorityItem(item)", "UPDATE_ITEM");
+                       // Note that we have to create the service context for the Items, not the main service
+            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
+                       String parentcsid;
+                       if(parentSpec.form==SpecifierForm.CSID) {
+                               parentcsid = parentSpec.value;
+                       } else {
+                               String whereClause = buildWhereForAuthByName(parentSpec.value);
+                   ctx = createServiceContext(getServiceName());
+                               parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
+                       }
+                       ctx = createServiceContext(getItemServiceName(), theUpdate);
+            String itemcsid;
+                       if(itemSpec.form==SpecifierForm.CSID) {
+                               itemcsid = itemSpec.value;
+                       } else {
+                               String itemWhereClause = 
+                                       buildWhereForAuthItemByName(itemSpec.value, parentcsid);
+                               itemcsid = getRepositoryClient(ctx).findDocCSID(ctx, itemWhereClause);
+                       }
+                       // Note that we have to create the service context for the Items, not the main service
+                       DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid);
+                       getRepositoryClient(ctx).update(ctx, itemcsid, handler);
+                       result = ctx.getOutput();
+               } catch (BadRequestException bre) {
+                       Response response = Response.status(
+                                       Response.Status.BAD_REQUEST).entity("Create failed reason " + bre.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Update failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (DocumentNotFoundException dnfe) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("caught DNF exception in updateAuthorityItem", dnfe);
+                       }
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Update failed on AuthorityItem csid=" + itemspecifier).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Update failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+               return result.getBytes();
+       }
+
+       /**
+        * Delete authorityItem.
+        * 
+        * @param parentcsid the parentcsid
+        * @param itemcsid the itemcsid
+        * 
+        * @return the response
+        */
+       @DELETE
+       @Path("{csid}/items/{itemcsid}")
+       public Response deleteAuthorityItem(
+                       @PathParam("csid") String parentcsid,
+                       @PathParam("itemcsid") String itemcsid) {
+               if (logger.isDebugEnabled()) {
+                       logger.debug("deleteAuthorityItem with parentcsid=" + parentcsid + " and itemcsid=" + itemcsid);
+               }
+               if (parentcsid == null || "".equals(parentcsid)) {
+                       logger.error("deleteVocabularyItem: missing csid!");
+                       Response response = Response.status(Response.Status.BAD_REQUEST).entity(
+                                       "delete failed on AuthorityItem parentcsid=" + parentcsid).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+               if (itemcsid == null || "".equals(itemcsid)) {
+                       logger.error("deleteVocabularyItem: missing itemcsid!");
+                       Response response = Response.status(Response.Status.BAD_REQUEST).entity(
+                                       "delete failed on AuthorityItem=" + itemcsid).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+               try {
+                       // Note that we have to create the service context for the Items, not the main service
+                       ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
+                       getRepositoryClient(ctx).delete(ctx, itemcsid);
+                       return Response.status(HttpResponseCodes.SC_OK).build();
+               } catch (UnauthorizedException ue) {
+                       Response response = Response.status(
+                                       Response.Status.UNAUTHORIZED).entity("Delete failed reason " + ue.getErrorReason()).type("text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (DocumentNotFoundException dnfe) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("caught exception in deleteAuthorityItem", dnfe);
+                       }
+                       Response response = Response.status(Response.Status.NOT_FOUND).entity(
+                                       "Delete failed on AuthorityItem itemcsid=" + itemcsid).type(
+                                       "text/plain").build();
+                       throw new WebApplicationException(response);
+               } catch (Exception e) {
+                       Response response = Response.status(
+                                       Response.Status.INTERNAL_SERVER_ERROR).entity("Delete failed").type("text/plain").build();
+                       throw new WebApplicationException(response);
+               }
+       }
+    
+}
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/IVocabManager.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/IVocabManager.java
new file mode 100644 (file)
index 0000000..c39206b
--- /dev/null
@@ -0,0 +1,7 @@
+package org.collectionspace.services.common.vocabulary;\r
+\r
+public interface IVocabManager {\r
+       \r
+       public void exampleMethod(String someParam);\r
+\r
+}\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java
new file mode 100644 (file)
index 0000000..0047851
--- /dev/null
@@ -0,0 +1,277 @@
+/**\r
+ *  This document is a part of the source code and related artifacts\r
+ *  for CollectionSpace, an open source collections management system\r
+ *  for museums and related institutions:\r
+\r
+ *  http://www.collectionspace.org\r
+ *  http://wiki.collectionspace.org\r
+\r
+ *  Copyright 2009 University of California at Berkeley\r
+\r
+ *  Licensed under the Educational Community License (ECL), Version 2.0.\r
+ *  You may not use this file except in compliance with this License.\r
+\r
+ *  You may obtain a copy of the ECL 2.0 License at\r
+\r
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt\r
+\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS,\r
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ */\r
+package org.collectionspace.services.common.vocabulary;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.nuxeo.ecm.core.api.ClientException;\r
+import org.nuxeo.ecm.core.api.DocumentModel;\r
+import org.nuxeo.ecm.core.api.DocumentModelList;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import org.collectionspace.services.common.ServiceMain;\r
+import org.collectionspace.services.common.context.ServiceContext;\r
+import org.collectionspace.services.common.authorityref.AuthorityRefDocList;\r
+import org.collectionspace.services.common.authorityref.AuthorityRefList;\r
+import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;\r
+import org.collectionspace.services.common.context.ServiceBindingUtils;\r
+import org.collectionspace.services.common.document.DocumentException;\r
+import org.collectionspace.services.common.document.DocumentNotFoundException;\r
+import org.collectionspace.services.common.document.DocumentUtils;\r
+import org.collectionspace.services.common.document.DocumentWrapper;\r
+import org.collectionspace.services.common.repository.RepositoryClient;\r
+import org.collectionspace.services.common.service.ServiceBindingType;\r
+import org.collectionspace.services.jaxb.AbstractCommonList;\r
+import org.collectionspace.services.nuxeo.util.NuxeoUtils;\r
+\r
+/**\r
+ * RefNameServiceUtils is a collection of services utilities related to refName usage.\r
+ *\r
+ * $LastChangedRevision: $\r
+ * $LastChangedDate: $\r
+ */\r
+public class RefNameServiceUtils {\r
+\r
+    private final Logger logger = LoggerFactory.getLogger(RefNameServiceUtils.class);\r
+\r
+    public static AuthorityRefDocList getAuthorityRefDocs(ServiceContext ctx,\r
+            RepositoryClient repoClient,\r
+            String serviceType,\r
+            String refName,\r
+            int pageSize, int pageNum, boolean computeTotal) throws DocumentException, DocumentNotFoundException {\r
+        AuthorityRefDocList wrapperList = new AuthorityRefDocList();\r
+        AbstractCommonList commonList = (AbstractCommonList) wrapperList;\r
+        commonList.setPageNum(pageNum);\r
+        commonList.setPageSize(pageSize);\r
+\r
+        \r
+        List<AuthorityRefDocList.AuthorityRefDocItem> list =\r
+                wrapperList.getAuthorityRefDocItem();\r
+        TenantBindingConfigReaderImpl tReader =\r
+                ServiceMain.getInstance().getTenantBindingConfigReader();\r
+        List<ServiceBindingType> servicebindings = tReader.getServiceBindingsByType(ctx.getTenantId(), serviceType);\r
+        if (servicebindings == null || servicebindings.isEmpty()) {\r
+            return null;\r
+        }\r
+        // Need to escape the quotes in the refName\r
+        // TODO What if they are already escaped?\r
+        String escapedRefName = refName.replaceAll("'", "\\\\'");\r
+//     String domain = \r
+//             tReader.getTenantBinding(ctx.getTenantId()).getRepositoryDomain();\r
+        ArrayList<String> docTypes = new ArrayList<String>();\r
+        Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();\r
+        Map<String, Map<String, String>> authRefFieldsByService = new HashMap<String, Map<String, String>>();\r
+        StringBuilder whereClause = new StringBuilder();\r
+        boolean fFirst = true;\r
+        List<String> authRefFieldPaths = new ArrayList<String>();\r
+        for (ServiceBindingType sb : servicebindings) {\r
+               // Gets the property names for each part, qualified with the part label (which\r
+               // is also the table name, the way that the repository works).\r
+            authRefFieldPaths =\r
+                    ServiceBindingUtils.getAllPartsPropertyValues(sb,\r
+                    ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);\r
+            if (authRefFieldPaths.isEmpty()) {\r
+                continue;\r
+            }\r
+            String authRefPath = "";\r
+            String ancestorAuthRefFieldName = "";\r
+            Map<String, String> authRefFields = new HashMap<String, String>();\r
+            for (int i = 0; i < authRefFieldPaths.size(); i++) {\r
+                // fieldName = DocumentUtils.getDescendantOrAncestor(authRefFields.get(i));\r
+               // For simple field values, we just search on the item.\r
+               // For simple repeating scalars, we just search the group field \r
+               // For repeating complex types, we will need to do more.\r
+                authRefPath = authRefFieldPaths.get(i);\r
+                ancestorAuthRefFieldName = DocumentUtils.getAncestorAuthRefFieldName(authRefFieldPaths.get(i));\r
+                authRefFields.put(authRefPath, ancestorAuthRefFieldName);\r
+            }\r
+\r
+            String docType = sb.getObject().getName();\r
+            queriedServiceBindings.put(docType, sb);\r
+            authRefFieldsByService.put(docType, authRefFields);\r
+            docTypes.add(docType);\r
+            Collection<String> fields = authRefFields.values();\r
+            for (String field : fields) {\r
+                // Build up the where clause for each authRef field\r
+                if (fFirst) {\r
+                    fFirst = false;\r
+                } else {\r
+                    whereClause.append(" OR ");\r
+                }\r
+                //whereClause.append(prefix);\r
+                whereClause.append(field);\r
+                whereClause.append("='");\r
+                whereClause.append(escapedRefName);\r
+                whereClause.append("'");\r
+            }\r
+        }\r
+        String whereClauseStr = whereClause.toString(); // for debugging\r
+        if (fFirst) // found no authRef fields - nothing to query\r
+        {\r
+            return wrapperList;\r
+        }\r
+        String fullQuery = whereClause.toString(); // for debug\r
+        // Now we have to issue the search\r
+        DocumentWrapper<DocumentModelList> docListWrapper = repoClient.findDocs(ctx,\r
+                docTypes, whereClause.toString(), pageSize, pageNum, computeTotal);\r
+        // Now we gather the info for each document into the list and return\r
+        DocumentModelList docList = docListWrapper.getWrappedObject();\r
+        // Set num of items in list. this is useful to our testing framework.\r
+        commonList.setItemsInPage(docList.size());\r
+        // set the total result size\r
+        commonList.setTotalItems(docList.totalSize());\r
+        Iterator<DocumentModel> iter = docList.iterator();\r
+        while (iter.hasNext()) {\r
+            DocumentModel docModel = iter.next();\r
+            AuthorityRefDocList.AuthorityRefDocItem ilistItem = new AuthorityRefDocList.AuthorityRefDocItem();\r
+            String csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());\r
+            String docType = docModel.getDocumentType().getName();\r
+            ServiceBindingType sb = queriedServiceBindings.get(docType);\r
+            if (sb == null) {\r
+                throw new RuntimeException(\r
+                        "getAuthorityRefDocs: No Service Binding for docType: " + docType);\r
+            }\r
+            String serviceContextPath = "/" + sb.getName().toLowerCase() + "/";\r
+            // The id and URI are the same on all doctypes\r
+            ilistItem.setDocId(csid);\r
+            ilistItem.setUri(serviceContextPath + csid);\r
+            ilistItem.setDocType(docType);\r
+            ilistItem.setDocNumber(\r
+                    ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel));\r
+            ilistItem.setDocName(\r
+                    ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel));\r
+            // Now, we have to loop over the authRefFieldsByService to figure\r
+            // out which field matched this. Ignore multiple matches.\r
+            Map<String,String> matchingAuthRefFields = authRefFieldsByService.get(docType);\r
+            if (matchingAuthRefFields == null || matchingAuthRefFields.isEmpty()) {\r
+                throw new RuntimeException(\r
+                        "getAuthorityRefDocs: internal logic error: can't fetch authRefFields for DocType.");\r
+            }\r
+            String authRefAncestorField = "";\r
+            String authRefDescendantField = "";\r
+            String sourceField = "";\r
+            boolean fRefFound = false;\r
+            // Use this if we go to qualified field names\r
+            for (String path : matchingAuthRefFields.keySet()) {\r
+                try {\r
+                       // This is the field name we show in the return info\r
+                    authRefAncestorField = (String) matchingAuthRefFields.get(path);\r
+                    // This is the qualified field we have to get from the doc model\r
+                    authRefDescendantField = DocumentUtils.getDescendantOrAncestor(path);\r
+                    // The ancestor field is part-schema (tablename) qualified\r
+                    String[] strings = authRefAncestorField.split(":");\r
+                    if (strings.length != 2) {\r
+                        throw new RuntimeException(\r
+                                "getAuthorityRefDocs: Bad configuration of path to authority reference field.");\r
+                    }\r
+                    // strings[0] holds a schema name, such as "intakes_common"\r
+                    //\r
+                    // strings[1] holds:\r
+                    // * The name of an authority reference field, such as "depositor";\r
+                    //   or\r
+                    // * The name of an ancestor (e.g. parent, grandparent ...) field,\r
+                    //   such as "fieldCollectors", of a repeatable authority reference\r
+                    //   field, such as "fieldCollector".\r
+                    // TODO - if the value is not simple, or repeating scalar, need a more\r
+                    // sophisticated fetch. \r
+                    Object fieldValue = docModel.getProperty(strings[0], strings[1]);\r
+                    // We cannot be sure why we have this doc, so look for matches\r
+                    boolean fRefMatches = refNameFoundInField(refName, fieldValue);\r
+                    if (fRefMatches) {\r
+                        sourceField = authRefDescendantField;\r
+                        // Handle multiple fields matching in one Doc. See CSPACE-2863.\r
+                       if(fRefFound) {\r
+                               // We already added ilistItem, so we need to clone that and add again\r
+                            ilistItem = cloneAuthRefDocItem(ilistItem, sourceField);\r
+                       } else {\r
+                               ilistItem.setSourceField(sourceField);\r
+                            fRefFound = true;\r
+                       }\r
+                        list.add(ilistItem);\r
+                    }\r
+\r
+                } catch (ClientException ce) {\r
+                    throw new RuntimeException(\r
+                            "getAuthorityRefDocs: Problem fetching: " + sourceField, ce);\r
+                }\r
+            }\r
+            if (!fRefFound) {\r
+                throw new RuntimeException(\r
+                        "getAuthorityRefDocs: Could not find refname in object:"\r
+                        + docType + ":" + csid);\r
+            }\r
+        }\r
+        return wrapperList;\r
+    }\r
+    \r
+    private static AuthorityRefDocList.AuthorityRefDocItem cloneAuthRefDocItem(\r
+               AuthorityRefDocList.AuthorityRefDocItem ilistItem, String sourceField) {\r
+       AuthorityRefDocList.AuthorityRefDocItem newlistItem = new AuthorityRefDocList.AuthorityRefDocItem();\r
+       newlistItem.setDocId(ilistItem.getDocId());\r
+       newlistItem.setDocName(ilistItem.getDocName());\r
+       newlistItem.setDocNumber(ilistItem.getDocNumber());\r
+       newlistItem.setDocType(ilistItem.getDocType());\r
+       newlistItem.setUri(ilistItem.getUri());\r
+       newlistItem.setSourceField(sourceField);\r
+       return newlistItem;\r
+    }\r
+\r
+    /*\r
+     * Identifies whether the refName was found in the supplied field.\r
+     *\r
+     * Only works for:\r
+     * * Scalar fields\r
+     * * Repeatable scalar fields (aka multi-valued fields)\r
+     *\r
+     * Does not work for:\r
+     * * Structured fields (complexTypes)\r
+     * * Repeatable structured fields (repeatable complexTypes)\r
+     */\r
+    private static boolean refNameFoundInField(String refName, Object fieldValue) {\r
+\r
+        boolean result = false;\r
+        if (fieldValue instanceof List) {\r
+            List<String> fieldValueList = (List) fieldValue;\r
+            for (String listItemValue : fieldValueList) {\r
+                if (refName.equalsIgnoreCase(listItemValue)) {\r
+                    result = true;\r
+                    break;\r
+                }\r
+\r
+            }\r
+        } else if (fieldValue instanceof String){\r
+            if (refName.equalsIgnoreCase((String)fieldValue)) {\r
+                result = true;\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+}\r
+\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/RefNameUtils.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/RefNameUtils.java
new file mode 100644 (file)
index 0000000..7a54b2e
--- /dev/null
@@ -0,0 +1,245 @@
+/**\r
+ *  This document is a part of the source code and related artifacts\r
+ *  for CollectionSpace, an open source collections management system\r
+ *  for museums and related institutions:\r
+\r
+ *  http://www.collectionspace.org\r
+ *  http://wiki.collectionspace.org\r
+\r
+ *  Copyright 2009 University of California at Berkeley\r
+\r
+ *  Licensed under the Educational Community License (ECL), Version 2.0.\r
+ *  You may not use this file except in compliance with this License.\r
+\r
+ *  You may obtain a copy of the ECL 2.0 License at\r
+\r
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt\r
+\r
+ *  Unless required by applicable law or agreed to in writing, software\r
+ *  distributed under the License is distributed on an "AS IS" BASIS,\r
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ *  See the License for the specific language governing permissions and\r
+ *  limitations under the License.\r
+ */\r
+package org.collectionspace.services.common.vocabulary;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+/**\r
+ * RefNameUtils is a collection of utilities related to refName URN strings\r
+ * refNames are URNs that reference a document entity, often an authority or \r
+ * authority term. They are strings that take the form (for authorities):\r
+ * urn:cspace:org.collectionspace.demo:vocabulary:name(Entry Methods)'Entry Methods'\r
+ * or the form (for authority terms):\r
+ * urn:cspace:org.collectionspace.demo:vocabulary:name(Entry Methods):item:name(Loan)'Loan'\r
+ *\r
+ * $LastChangedRevision: $\r
+ * $LastChangedDate: $\r
+ */\r
+public class RefNameUtils {\r
+\r
+    private final Logger logger = LoggerFactory.getLogger(RefNameUtils.class);\r
+\r
+    public static final String URN_PREFIX = "urn:cspace:";\r
+    public static final int URN_PREFIX_LEN = 11;\r
+    public static final String URN_NAME_PREFIX = "urn:cspace:name(";\r
+    public static final int URN_NAME_PREFIX_LEN = 16;\r
+       // FIXME Should not be hard-coded\r
+    private static final String ITEMS_REGEX = "item|person|organization";\r
+    // In a list of tokens, these are indices for each part\r
+    private static final int DOMAIN_TOKEN = 0;                         // e.g., 'org.collectionspace.demo'\r
+    private static final int RESOURCE_TOKEN = 1;               // vocabulary, personauthority, etc.\r
+    private static final int AUTH_INSTANCE_TOKEN = 2;  // name(Entry Methods)'Entry Methods'\r
+    private static final int ITEMS_TOKEN = 3;                  // 'item', 'person', etc.\r
+    private static final int ITEM_INSTANCE_TOKEN = 4;  // name(Entry Methods)'Entry Methods'\r
+    // Tokenizing the INSTANCE, these are indices for each item-part\r
+    private static final int INSTANCE_SPEC_TYPE_TOKEN = 0;     // 'name' or 'id' \r
+    private static final int INSTANCE_SPEC_TOKEN = 1;          // name or id value\r
+    private static final int INSTANCE_DISPLAYNAME_TOKEN = 2;// optional displayName suffix\r
+    private static final int INSTANCE_TOKENS_MIN = 2;\r
+    private static final int INSTANCE_TOKENS_MAX = 3;\r
+    public static final String SEPARATOR = ":";\r
+\r
+    public static class AuthorityInfo {\r
+        private final Logger logger = LoggerFactory.getLogger(AuthorityInfo.class);\r
+       private static int MIN_TOKENS = 3;\r
+        public String domain;\r
+        public String resource;\r
+        public String csid;\r
+        public String name;\r
+        public String displayName;\r
+        \r
+        public AuthorityInfo(String refNameTokens[]) throws Exception {\r
+               try {\r
+                       if(refNameTokens.length < MIN_TOKENS) {\r
+                               throw new IllegalArgumentException("Malformed refName for Authority (too few tokens)");\r
+                       }\r
+                       this.domain = refNameTokens[DOMAIN_TOKEN]; \r
+                       this.resource = refNameTokens[RESOURCE_TOKEN];\r
+                       String idTokens[] = refNameTokens[AUTH_INSTANCE_TOKEN].split("[()]", INSTANCE_TOKENS_MAX);\r
+                       if(idTokens.length<INSTANCE_TOKENS_MIN) {\r
+                               throw new IllegalArgumentException("Missing/malformed identifier");\r
+                       }\r
+                       if(idTokens[INSTANCE_SPEC_TYPE_TOKEN].equals("name")) {\r
+                               this.name = idTokens[INSTANCE_SPEC_TOKEN];\r
+                               this.csid = null;\r
+                       } else if(idTokens[INSTANCE_SPEC_TYPE_TOKEN].startsWith("id")) {\r
+                               this.csid = idTokens[INSTANCE_SPEC_TOKEN];\r
+                               this.name = null;\r
+                       } else {\r
+                               throw new IllegalArgumentException("Identifier type must be 'name' or 'id'");\r
+                       }\r
+                       // displayName is always in quotes, so must have at least 3 chars\r
+                       this.displayName = \r
+                               ((idTokens.length<INSTANCE_TOKENS_MAX)||(idTokens[INSTANCE_DISPLAYNAME_TOKEN].length()<3))? null:\r
+                               idTokens[INSTANCE_DISPLAYNAME_TOKEN].substring(1, idTokens[INSTANCE_DISPLAYNAME_TOKEN].length()-1);\r
+               } catch (Exception e) {\r
+                   if (logger.isDebugEnabled()) {\r
+                       logger.debug("Problem Building AuthorityInfo from tokens: " \r
+                                       + RefNameUtils.implodeStringArray(refNameTokens, ", "));\r
+                   }\r
+                   throw e;\r
+               }\r
+        }\r
+        \r
+        public String getRelativeUri() {\r
+               StringBuilder uri = new StringBuilder();\r
+               // FIXME This should not be hard-coded.\r
+               if(resource.equals("vocabulary")) {\r
+                       uri.append("/vocabularies/");\r
+               } else if(resource.equals("personauthority")) {\r
+                       uri.append("/personauthorities/");\r
+               } else if(resource.equals("orgauthority")) {\r
+                       uri.append("/orgauthorities/");\r
+               } else {\r
+                       throw new RuntimeException("Illegal Authority Type: " + resource);\r
+               }\r
+               if(csid!=null) {\r
+                       uri.append(csid);\r
+               } else if(name!=null) {\r
+                       uri.append("urn:cspace:name("+name+")");\r
+               } else {\r
+                       throw new RuntimeException("Missing id/name specifier");\r
+               }\r
+               return uri.toString();\r
+        }\r
+    };\r
+\r
+    public static class AuthorityTermInfo {\r
+        private final Logger logger = LoggerFactory.getLogger(AuthorityTermInfo.class);\r
+       private static int MIN_TOKENS = 5;\r
+       public AuthorityInfo inAuthority;\r
+       public String csid;\r
+       public String name;\r
+       public String displayName;\r
+        \r
+        public AuthorityTermInfo(String refNameTokens[]) throws Exception {\r
+               try {\r
+                       if(refNameTokens.length < MIN_TOKENS) {\r
+                               throw new IllegalArgumentException("Malformed refName for AuthorityTerm (too few tokens)");\r
+                       }\r
+                       this.inAuthority = new AuthorityInfo(refNameTokens); \r
+                       if(!refNameTokens[ITEMS_TOKEN].matches(ITEMS_REGEX)) {\r
+                               throw new IllegalArgumentException("Item spec must be one of: "+ITEMS_REGEX);\r
+                       }\r
+                       String idTokens[] = refNameTokens[ITEM_INSTANCE_TOKEN].split("[\\(\\)]", 3);\r
+                       if(idTokens.length<INSTANCE_TOKENS_MIN) {\r
+                               throw new IllegalArgumentException("Missing/malformed identifier");\r
+                       }\r
+                       if(idTokens[INSTANCE_SPEC_TYPE_TOKEN].equals("name")) {\r
+                               this.name = idTokens[INSTANCE_SPEC_TOKEN];\r
+                               this.csid = null;\r
+                       } else if(idTokens[INSTANCE_SPEC_TYPE_TOKEN].startsWith("id")) {\r
+                               this.csid = idTokens[INSTANCE_SPEC_TOKEN];\r
+                               this.name = null;\r
+                       } else {\r
+                               throw new IllegalArgumentException("Identifier type must be 'name' or 'id'");\r
+                       }\r
+                       // displayName is always in quotes, so must have at least 3 chars\r
+                       this.displayName = \r
+                               ((idTokens.length<INSTANCE_TOKENS_MAX)||(idTokens[INSTANCE_DISPLAYNAME_TOKEN].length()<3))? null:\r
+                               idTokens[INSTANCE_DISPLAYNAME_TOKEN].substring(1, idTokens[INSTANCE_DISPLAYNAME_TOKEN].length()-1);\r
+               } catch (Exception e) {\r
+                   if (logger.isDebugEnabled()) {\r
+                       logger.debug("Problem Building AuthorityTermInfo from tokens: " \r
+                                       + RefNameUtils.implodeStringArray(refNameTokens, ", "));\r
+                   }\r
+                   throw e;\r
+               }\r
+        }\r
+        \r
+        public String getRelativeUri() {\r
+               StringBuilder uri = new StringBuilder(inAuthority.getRelativeUri()+"/items/");\r
+               if(csid!=null) {\r
+                       uri.append(csid);\r
+               } else if(name!=null) {\r
+                       uri.append("urn:cspace:name("+name+")");\r
+               } else {\r
+                       throw new RuntimeException("Missing id/name specifier");\r
+               }\r
+               return uri.toString();\r
+        }\r
+    };\r
+\r
+    public static AuthorityInfo parseAuthorityInfo(String refName)\r
+            throws Exception {\r
+       if(refName==null || !refName.startsWith(URN_PREFIX))\r
+               throw new RuntimeException( "Null or invalid refName syntax");\r
+       return new AuthorityInfo(refName.substring(URN_PREFIX_LEN).split(SEPARATOR));\r
+    }\r
+\r
+    public static AuthorityTermInfo parseAuthorityTermInfo(String refName)\r
+            throws Exception {\r
+       if(refName==null || !refName.startsWith(URN_PREFIX))\r
+               throw new RuntimeException( "Null or invalid refName syntax");\r
+       return new AuthorityTermInfo(refName.substring(URN_PREFIX_LEN).split(SEPARATOR));\r
+    }\r
+\r
+    public static String implodeStringArray(String tokens[], String separator) {\r
+       if (tokens.length==0) {\r
+               return "";\r
+       } else {\r
+               StringBuffer sb = new StringBuffer();\r
+               sb.append(tokens[0]);\r
+               for (int i=1;i<tokens.length;i++) {\r
+                       sb.append(separator);\r
+                       sb.append(tokens[i]);\r
+               }\r
+               return sb.toString();\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Returns the name / shortIdentifier value of an authority item in a refName\r
+     */\r
+    public static String getItemShortId(String refName) {\r
+        String name = "";\r
+        try {\r
+            String [] refNameTokens = refName.substring(URN_PREFIX_LEN).split(SEPARATOR);\r
+            AuthorityTermInfo authTermInfo = new AuthorityTermInfo(refNameTokens);\r
+            name = authTermInfo.name;\r
+        } catch(Exception e) {\r
+            // do nothing\r
+        }\r
+        return name;\r
+    }\r
+\r
+    /**\r
+     * Creates a refName in the name / shortIdentifier form.\r
+     *\r
+     * @param shortId a shortIdentifier for an authority or one of its terms\r
+     * @return a refName for that authority or term, in the name / shortIdentifier form.\r
+     *         If the provided shortIdentifier is null or empty, returns\r
+     *         the empty string.\r
+     */\r
+    public static String createShortIdRefName(String shortId) {\r
+        if (shortId == null || shortId.trim().isEmpty()) {\r
+            return "";\r
+        } else {\r
+            return "urn:cspace:name("+shortId+")";\r
+        }\r
+    }\r
+    \r
+}\r
+\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/VocabManager.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/VocabManager.java
new file mode 100644 (file)
index 0000000..8495990
--- /dev/null
@@ -0,0 +1,9 @@
+package org.collectionspace.services.common.vocabulary;\r
+\r
+public class VocabManager {\r
+       static private final IVocabManager vocabManager = new VocabManagerImpl();\r
+       \r
+       static public void exampleMethod(String someParam) {\r
+               vocabManager.exampleMethod(someParam);\r
+       }\r
+}\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/VocabManagerImpl.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/VocabManagerImpl.java
new file mode 100644 (file)
index 0000000..d00d923
--- /dev/null
@@ -0,0 +1,22 @@
+package org.collectionspace.services.common.vocabulary;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import org.nuxeo.ecm.core.api.DocumentModel;\r
+import org.nuxeo.ecm.core.api.DocumentModelList;\r
+import org.nuxeo.ecm.core.api.repository.RepositoryInstance;\r
+import org.nuxeo.ecm.core.client.NuxeoClient;\r
+\r
+import org.collectionspace.services.nuxeo.client.java.NuxeoConnector;\r
+import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;\r
+//import org.collectionspace.services.common.query.IQueryManager;\r
+\r
+public class VocabManagerImpl implements IVocabManager {\r
+       \r
+       private final Logger logger = LoggerFactory\r
+                       .getLogger(VocabManagerImpl.class);\r
+       \r
+       public void exampleMethod(String someParam) {\r
+       }\r
+}\r
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java
new file mode 100644 (file)
index 0000000..f910a13
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ *  This document is a part of the source code and related artifacts
+ *  for CollectionSpace, an open source collections management system
+ *  for museums and related institutions:
+
+ *  http://www.collectionspace.org
+ *  http://wiki.collectionspace.org
+
+ *  Copyright 2009 University of California at Berkeley
+
+ *  Licensed under the Educational Community License (ECL), Version 2.0.
+ *  You may not use this file except in compliance with this License.
+
+ *  You may obtain a copy of the ECL 2.0 License at
+
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt
+
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.collectionspace.services.common.vocabulary.nuxeo;
+
+import java.util.Map;
+
+import org.collectionspace.services.common.document.DocumentWrapper;
+import org.collectionspace.services.common.service.ObjectPartType;
+
+import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
+import org.collectionspace.services.nuxeo.util.NuxeoUtils;
+import org.nuxeo.ecm.core.api.DocumentModel;
+
+/**
+ * AuthorityDocumentModelHandler
+ *
+ * $LastChangedRevision: $
+ * $LastChangedDate: $
+ */
+public abstract class AuthorityDocumentModelHandler<AuthCommon, AuthCommonList>
+        extends RemoteDocumentModelHandlerImpl<AuthCommon, AuthCommonList> {
+
+       private String authorityCommonSchemaName;
+       
+    /**
+     * authority is used to stash JAXB object to use when handle is called
+     * for Action.CREATE, Action.UPDATE or Action.GET
+     */
+    private AuthCommon authority;
+    /**
+     * authorityList is stashed when handle is called
+     * for ACTION.GET_ALL
+     */
+    private AuthCommonList authorityList;
+
+
+    public AuthorityDocumentModelHandler(String authorityCommonSchemaName) {
+       this.authorityCommonSchemaName = authorityCommonSchemaName;
+    }
+
+    /**
+     * getCommonPart get associated authority
+     * @return
+     */
+    @Override
+    public AuthCommon getCommonPart() {
+        return authority;
+    }
+
+    /**
+     * setCommonPart set associated authority
+     * @param authority
+     */
+    @Override
+    public void setCommonPart(AuthCommon authority) {
+        this.authority = authority;
+    }
+
+    /**
+     * getCommonPartList get associated authority (for index/GET_ALL)
+     * @return
+     */
+    @Override
+    public AuthCommonList getCommonPartList() {
+        return authorityList;
+    }
+
+    /* (non-Javadoc)
+     * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#setCommonPartList(java.lang.Object)
+     */
+    @Override
+    public void setCommonPartList(AuthCommonList authorityList) {
+        this.authorityList = authorityList;
+    }
+
+    /* (non-Javadoc)
+     * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#extractCommonPart(org.collectionspace.services.common.document.DocumentWrapper)
+     */
+    @Override
+    public AuthCommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
+            throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    /* (non-Javadoc)
+     * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#fillCommonPart(java.lang.Object, org.collectionspace.services.common.document.DocumentWrapper)
+     */
+    @Override
+    public void fillCommonPart(AuthCommon vocabularyObject, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    /* (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
+       if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
+               String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
+               unQObjectProperties.put("csid", csid);
+       }
+       
+       return unQObjectProperties;
+    }
+    
+}
+
diff --git a/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java b/services/authority/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java
new file mode 100644 (file)
index 0000000..6421c54
--- /dev/null
@@ -0,0 +1,174 @@
+/**
+ *  This document is a part of the source code and related artifacts
+ *  for CollectionSpace, an open source collections management system
+ *  for museums and related institutions:
+
+ *  http://www.collectionspace.org
+ *  http://wiki.collectionspace.org
+
+ *  Copyright 2009 University of California at Berkeley
+
+ *  Licensed under the Educational Community License (ECL), Version 2.0.
+ *  You may not use this file except in compliance with this License.
+
+ *  You may obtain a copy of the ECL 2.0 License at
+
+ *  https://source.collectionspace.org/collection-space/LICENSE.txt
+
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.collectionspace.services.common.vocabulary.nuxeo;
+
+import java.util.Map;
+
+import org.collectionspace.services.common.document.DocumentWrapper;
+import org.collectionspace.services.common.service.ObjectPartType;
+import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
+import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
+import org.collectionspace.services.nuxeo.util.NuxeoUtils;
+import org.nuxeo.ecm.core.api.DocumentModel;
+
+/**
+ * AuthorityItemDocumentModelHandler
+ *
+ * $LastChangedRevision: $
+ * $LastChangedDate: $
+ */
+public abstract class AuthorityItemDocumentModelHandler<AICommon, AICommonList>
+        extends RemoteDocumentModelHandlerImpl<AICommon, AICommonList> {
+
+       private String authorityItemCommonSchemaName;
+       
+    //private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
+    /**
+     * item is used to stash JAXB object to use when handle is called
+     * for Action.CREATE, Action.UPDATE or Action.GET
+     */
+    protected AICommon item;
+    /**
+     * itemList is stashed when handle is called
+     * for ACTION.GET_ALL
+     */
+    protected AICommonList itemList;
+    
+    /**
+     * inVocabulary is the parent Authority for this context
+     */
+    protected String inAuthority;
+    
+    public AuthorityItemDocumentModelHandler(String authorityItemCommonSchemaName) {
+       this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
+    }
+
+    public String getInAuthority() {
+               return inAuthority;
+       }
+
+       public void setInAuthority(String inAuthority) {
+               this.inAuthority = inAuthority;
+       }
+
+    /* (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
+       super.handleCreate(wrapDoc);            
+       handleInAuthority(wrapDoc.getWrappedObject());
+    }
+    
+    /**
+     * 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 {
+       docModel.setProperty(authorityItemCommonSchemaName, 
+                       AuthorityItemJAXBSchema.IN_AUTHORITY, inAuthority);
+    }
+
+
+    /**
+     * getCommonPart get associated item
+     * @return
+     */
+    @Override
+    public AICommon getCommonPart() {
+        return item;
+    }
+
+    /**
+     * setCommonPart set associated item
+     * @param vocabularyItem
+     */
+    @Override
+    public void setCommonPart(AICommon item) {
+        this.item = item;
+    }
+
+    /**
+     * getCommonPartList get associated item (for index/GET_ALL)
+     * @return
+     */
+    @Override
+    public AICommonList getCommonPartList() {
+        return itemList;
+    }
+
+    @Override
+    public void setCommonPartList(AICommonList itemList) {
+        this.itemList = itemList;
+    }
+
+    @Override
+    public AICommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
+            throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void fillCommonPart(AICommon itemObject, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+        throw new UnsupportedOperationException();
+    }
+    
+    /* (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
+       if (partMeta.getLabel().equalsIgnoreCase(authorityItemCommonSchemaName)) {
+               String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
+               unQObjectProperties.put("csid", csid);
+       }
+       
+       return unQObjectProperties;
+    }
+    
+    /**
+     * Filters out AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure that
+     * the parent link remains untouched.
+     * @param objectProps the properties parsed 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);
+       objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
+       objectProps.remove(AuthorityItemJAXBSchema.CSID);
+    }
+
+}
+