--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<xmlReplay>\r
+ <auths>\r
+ <!-- IMPORTANT: THESE ARE STICKY :: THEY STICK AROUND UNTIL RESET, IN EXEC ORDER OF THIS FILE. -->\r
+ <auth ID="admin@collectionspace.org">YWRtaW5AY29sbGVjdGlvbnNwYWNlLm9yZzpBZG1pbmlzdHJhdG9y</auth>\r
+ <auth ID="testAdministator">YWRtaW5AY29sbGVjdGlvbnNwYWNlLm9yZzpBZG1pbmlzdHJhdG9y</auth>\r
+ </auths>\r
+ \r
+ <testGroup ID="primary" autoDeletePOSTS="false">\r
+ <test ID="ba1" auth="test">\r
+ <method>POST</method>\r
+ <uri>/cspace-services/batch/</uri>\r
+ <filename>batch/batch1.xml</filename>\r
+ </test>\r
+ <test ID="ba2" auth="test">\r
+ <method>POST</method>\r
+ <uri>/cspace-services/collectionobjects/</uri>\r
+ <filename>batch/collObj1.xml</filename>\r
+ </test>\r
+ <test ID="ba3" auth="test">\r
+ <method>POST</method>\r
+ <uri>/cspace-services/batch/${ba1.CSID}</uri>\r
+ <filename>batch/batch1InvContext.xml</filename>\r
+ <vars>\r
+ <var ID="CollObj1">${ba2.CSID}</var>\r
+ </vars>\r
+ </test>\r
+ </testGroup> \r
+\r
+ <testGroup ID="cleanup" autoDeletePOSTS="true">\r
+ <test ID="cl1" auth="test">\r
+ <method>DELETE</method>\r
+ <uri>/cspace-services/batch/${ba1.CSID}</uri>\r
+ </test>\r
+ <test ID="cl2" auth="test">\r
+ <method>DELETE</method>\r
+ <uri>/cspace-services/collectionobjects/${ba2.CSID}</uri>\r
+ </test>\r
+ </testGroup>\r
+ \r
+\r
+</xmlReplay>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<document name="batch">\r
+ <ns2:batch_common\r
+ xmlns:ns2="http://collectionspace.org/services/batch"\r
+ xmlns:ns3="http://collectionspace.org/services/jaxb">\r
+ <name>TestCreateAndLinkLoanOutBatchJob</name>\r
+ <notes>This should be interesting</notes>\r
+ <forDocType>CollectionObject</forDocType>\r
+ <forSingleDoc>true</forSingleDoc>\r
+ <createsNewFocus>true</createsNewFocus>\r
+ <className>org.collectionspace.services.batch.nuxeo.CreateAndLinkLoanOutBatchJob</className>\r
+ </ns2:batch_common>\r
+</document>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<ns2:invocationContext\r
+xmlns:ns2="http://collectionspace.org/services/common/invocable"\r
+xmlns:ns3="http://collectionspace.org/services/jaxb">\r
+ <mode>single</mode>\r
+ <docType>CollectionObject</docType>\r
+ <singleCSID>${CollObj1}</singleCSID>\r
+</ns2:invocationContext>\r
+\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<document name="collectionobjects">
+<ns2:collectionobjects_common xmlns:ns2="http://collectionspace.org/services/collectionobject">
+ <objectNumber>objectNumber</objectNumber>
+ <otherNumber>XXX</otherNumber>
+ <otherNumberType>otherNumberType</otherNumberType>
+</ns2:collectionobjects_common>
+</document>
+
--- /dev/null
+package org.collectionspace.services.jaxrs;\r
+\r
+import javax.servlet.ServletContextEvent;\r
+\r
+import org.jboss.resteasy.core.Dispatcher;\r
+import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap;\r
+import org.collectionspace.services.common.ResourceMap;\r
+\r
+public class CSpaceResteasyBootstrap extends ResteasyBootstrap {\r
+ \r
+ public void contextInitialized(ServletContextEvent event) {\r
+ super.contextInitialized(event);\r
+ CollectionSpaceJaxRsApplication app = \r
+ (CollectionSpaceJaxRsApplication)deployment.getApplication();\r
+ Dispatcher disp = deployment.getDispatcher();\r
+ disp.getDefaultContextObjects().put(ResourceMap.class, app.getResourceMap());\r
+ }\r
+\r
+}\r
//import org.collectionspace.services.query.QueryResource;
import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
//import org.collectionspace.services.common.FileUtils;
import org.collectionspace.services.authorization.PermissionResource;
import org.collectionspace.services.authorization.RoleResource;
+import org.collectionspace.services.common.ResourceBase;
+import org.collectionspace.services.common.ResourceMap;
+import org.collectionspace.services.common.ResourceMapHolder;
+import org.collectionspace.services.common.ResourceMapImpl;
import org.collectionspace.services.common.security.SecurityInterceptor;
+import org.jboss.resteasy.core.Dispatcher;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
//import org.collectionspace.services.common.document.DocumentUtils;
//import org.collectionspace.services.common.imaging.nuxeo.NuxeoImageUtils;
//import org.collectionspace.services.common.profile.Profiler;
* $LastChangedRevision$
* $LastChangedDate$
*/
-public class CollectionSpaceJaxRsApplication extends Application {
+public class CollectionSpaceJaxRsApplication extends Application
+ implements ResourceMapHolder {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> empty = new HashSet<Class<?>>();
+ private ResourceMap resourceMap = new ResourceMapImpl();
public CollectionSpaceJaxRsApplication() {
//
// Instantiate all our JaxRS resources
//
+ ResourceBase resource;
singletons.add(new SecurityInterceptor());
singletons.add(new AccountResource());
singletons.add(new RoleResource());
singletons.add(new PermissionResource());
+
singletons.add(new VocabularyResource());
- singletons.add(new ContactResource());
singletons.add(new PersonAuthorityResource());
singletons.add(new OrgAuthorityResource());
- singletons.add(new CollectionObjectResource());
- singletons.add(new GroupResource());
- singletons.add(new IntakeResource());
- singletons.add(new DimensionResource());
- singletons.add(new RelationResource());
- singletons.add(new NoteResource());
- singletons.add(new LoaninResource());
- singletons.add(new LoanoutResource());
- singletons.add(new AcquisitionResource());
- singletons.add(new ObjectExitResource());
- singletons.add(new BatchResource());
- singletons.add(new ImportsResource());
- singletons.add(new MediaResource());
- singletons.add(new BlobResource());
- singletons.add(new MovementResource());
- singletons.add(new ReportResource());
singletons.add(new LocationAuthorityResource());
singletons.add(new TaxonomyAuthorityResource());
+
+ singletons.add(new AcquisitionResource());
+
+ addResourceToMapAndSingletons(new ContactResource());
+ addResourceToMapAndSingletons(new CollectionObjectResource());
+ addResourceToMapAndSingletons(new GroupResource());
+ addResourceToMapAndSingletons(new IntakeResource());
+ addResourceToMapAndSingletons(new DimensionResource());
+ addResourceToMapAndSingletons(new RelationResource());
+ addResourceToMapAndSingletons(new NoteResource());
+ addResourceToMapAndSingletons(new LoaninResource());
+ addResourceToMapAndSingletons(new LoanoutResource());
+ addResourceToMapAndSingletons(new ObjectExitResource());
+ addResourceToMapAndSingletons(new BatchResource());
+ addResourceToMapAndSingletons(new ImportsResource());
+ addResourceToMapAndSingletons(new MediaResource());
+ addResourceToMapAndSingletons(new BlobResource());
+ addResourceToMapAndSingletons(new MovementResource());
+ addResourceToMapAndSingletons(new ReportResource());
+
singletons.add(new IDResource());
/*
singletons.add(new WorkflowResource());
// singletons.add(new DomainIdentifierResource());
// singletons.add(new PingResource());
}
+
+ private void addResourceToMapAndSingletons(ResourceBase resource) {
+ singletons.add(resource);
+ resourceMap.put(resource.getClass().getName(), resource);
+ }
@Override
public Set<Class<?>> getClasses() {
public Set<Object> getSingletons() {
return singletons;
}
+
+ public ResourceMap getResourceMap() {
+ return resourceMap;
+ }
}
\r
<listener>\r
<listener-class>\r
- org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap\r
+ org.collectionspace.services.jaxrs.CSpaceResteasyBootstrap\r
</listener-class>\r
</listener>\r
\r
public interface BatchJAXBSchema {
final static String BATCH_NAME = "name";
- final static String BATCH_FORDOCTYPE = "forDocType";
+ final static String BATCH_NOTES = "notes";
+ final static String BATCH_FOR_DOC_TYPE = "forDocType";
+ final static String BATCH_FOR_SINGLE_DOC = "forSingleDoc";
+ final static String BATCH_CREATES_NEW_FOCUS = "createsNewFocus";
+ final static String BATCH_CLASS_NAME = "className";
}
<artifactId>org.collectionspace.services.common</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.collectionspace.services</groupId>
+ <artifactId>org.collectionspace.services.jaxb</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.collectionspace.services</groupId>
<artifactId>org.collectionspace.services.batch.client</artifactId>
--- /dev/null
+package org.collectionspace.services.batch;\r
+\r
+import java.util.HashMap;\r
+import java.util.Set;\r
+\r
+import org.collectionspace.services.common.ResourceBase;\r
+import org.collectionspace.services.common.ResourceMap;\r
+import org.collectionspace.services.common.invocable.Invocable;\r
+\r
+public interface BatchInvocable extends Invocable {\r
+\r
+ /**\r
+ * Sets the invocation context for the batch job. Called before run().\r
+ * @param context an instance of InvocationContext.\r
+ */\r
+ public void setResourceMap(ResourceMap resourceMap);\r
+\r
+}\r
*/
package org.collectionspace.services.batch;
+import java.util.List;
+
+import org.collectionspace.services.BatchJAXBSchema;
import org.collectionspace.services.client.BatchClient;
+import org.collectionspace.services.client.PoxPayloadIn;
+import org.collectionspace.services.client.PoxPayloadOut;
import org.collectionspace.services.common.ResourceBase;
+import org.collectionspace.services.common.ResourceMap;
+import org.collectionspace.services.common.ServiceMessages;
+import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.DocumentHandler;
+import org.collectionspace.services.common.document.DocumentWrapper;
+import org.collectionspace.services.common.document.ValidatorHandler;
+import org.collectionspace.services.common.invocable.Invocable;
+import org.collectionspace.services.common.invocable.InvocationContext;
+import org.collectionspace.services.common.invocable.InvocationResults;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.nuxeo.ecm.core.api.DocumentModel;
+import org.collectionspace.services.common.ResourceMapHolder;
import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
@Path(BatchClient.SERVICE_PATH)
@Produces({"application/xml"})
}
}
+ @POST
+ @Path("{csid}")
+ public InvocationResults invokeBatchJob(
+ @Context ResourceMap resourceMap,
+ @PathParam("csid") String csid,
+ InvocationContext invContext) {
+ try {
+ ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
+ DocumentHandler handler = createDocumentHandler(ctx);
+ DocumentWrapper<DocumentModel> wrapper =
+ getRepositoryClient(ctx).getDoc(ctx, csid);
+ DocumentModel docModel = wrapper.getWrappedObject();
+ String className =
+ (String)docModel.getPropertyValue(BatchJAXBSchema.BATCH_CLASS_NAME);
+ className = className.trim();
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ Class<?> c = tccl.loadClass(className);
+ // enable validation assertions
+ tccl.setClassAssertionStatus(className, true);
+ if(!BatchInvocable.class.isAssignableFrom(c)) {
+ throw new RuntimeException("BatchResource: Class: "
+ +className+" does not implement BatchInvocable!");
+ } else {
+ BatchInvocable batchInstance = (BatchInvocable)c.newInstance();
+ List<String> modes = batchInstance.getSupportedInvocationModes();
+ if(!modes.contains(invContext.getMode())) {
+ throw new RuntimeException(
+ "BatchResource: Invoked with unsupported context mode: "
+ +invContext.getMode());
+ }
+ String forDocType =
+ (String)docModel.getPropertyValue(BatchJAXBSchema.BATCH_FOR_DOC_TYPE);
+ if(!forDocType.equalsIgnoreCase(invContext.getDocType())) {
+ throw new RuntimeException(
+ "BatchResource: Invoked with unsupported document type: "
+ +invContext.getDocType());
+ }
+ batchInstance.setInvocationContext(invContext);
+ //ResourceMapHolder csapp = (ResourceMapHolder)app;
+ if(resourceMap!=null) {
+ batchInstance.setResourceMap(resourceMap);
+ } else {
+ resourceMap = ResteasyProviderFactory.getContextData(ResourceMap.class);
+ if(resourceMap!=null) {
+ batchInstance.setResourceMap(resourceMap);
+ } else {
+ logger.warn("BatchResource.invoke did not get a resourceMapHolder in Context!");
+ }
+ }
+ batchInstance.run();
+ int status = batchInstance.getCompletionStatus();
+ if(status == Invocable.STATUS_ERROR) {
+ throw new RuntimeException(
+ "BatchResouce: batchProcess encountered error: "
+ +batchInstance.getErrorInfo());
+ }
+ InvocationResults results = batchInstance.getResults();
+ return results;
+ }
+ } catch (Exception e) {
+ throw bigReThrow(e, ServiceMessages.POST_FAILED);
+ }
+ }
}
public void validate(Action action, ServiceContext ctx)
throws InvalidDocumentException {
// TODO Auto-generated method stub
- System.out.println("BatchValidatorHandler executed.");
-
+ //System.out.println("BatchValidatorHandler executed.");
}
}
--- /dev/null
+package org.collectionspace.services.batch.nuxeo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.collectionspace.services.batch.BatchInvocable;
+import org.collectionspace.services.common.ResourceBase;
+import org.collectionspace.services.common.ResourceMap;
+import org.collectionspace.services.common.invocable.InvocationContext;
+import org.collectionspace.services.common.invocable.InvocationResults;
+
+public class CreateAndLinkLoanOutBatchJob implements BatchInvocable {
+
+ private static ArrayList<String> invocationModes = null;
+ private InvocationContext context;
+ private int completionStatus;
+ private HashMap<String,ResourceBase> resourceMap;
+ private InvocationResults results;
+ private String errorInfo;
+
+ public CreateAndLinkLoanOutBatchJob() {
+ CreateAndLinkLoanOutBatchJob.setupClassStatics();
+ context = null;
+ completionStatus = STATUS_UNSTARTED;
+ resourceMap = null;
+ results = new InvocationResults();
+ errorInfo = "";
+ }
+
+ private static void setupClassStatics() {
+ if(invocationModes == null ) {
+ invocationModes = new ArrayList<String>(1);
+ invocationModes.add(INVOCATION_MODE_SINGLE);
+ }
+ }
+
+ /**
+ * @return a set of modes that this plugin can support on invocation. Must be non-empty.
+ */
+ public List<String> getSupportedInvocationModes() {
+ return CreateAndLinkLoanOutBatchJob.invocationModes;
+ }
+
+ /**
+ * Sets the invocation context for the batch job. Called before run().
+ * @param context an instance of InvocationContext.
+ */
+ public void setInvocationContext(InvocationContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Sets the invocation context for the batch job. Called before run().
+ * @param context an instance of InvocationContext.
+ */
+ public void setResourceMap(ResourceMap resourceMap) {
+ }
+
+ /**
+ * The main work logic of the batch job. Will be called after setContext.
+ */
+ public void run() {
+ completionStatus = STATUS_UNSTARTED;
+ try {
+ Thread.sleep(1000);
+ } catch(Exception e) {}
+ results.setPrimaryURICreated(null);
+ results.setNumAffected(0);
+ results.setUserNote("CreateAndLinkLoanOutBatchJob pretended to do work, and completed");
+ completionStatus = STATUS_COMPLETE;
+ }
+
+ /**
+ * @return one of the STATUS_* constants, or a value from 1-99 to indicate progress.
+ * Implementations need not support partial completion (progress) values, and can transition
+ * from STATUS_MIN_PROGRESS to STATUS_COMPLETE.
+ */
+ public int getCompletionStatus() {
+ return completionStatus;
+ }
+
+ /**
+ * @return information about the batch job actions and results
+ */
+ public InvocationResults getResults() {
+ if(completionStatus != STATUS_COMPLETE)
+ return null;
+ return results;
+ }
+
+ /**
+ * @return a user-presentable note when an error occurs in batch processing. Will only
+ * be called if getCompletionStatus() returns STATUS_ERROR.
+ */
+ public String getErrorInfo() {
+ return errorInfo;
+ }
+
+
+}
--- /dev/null
+package org.collectionspace.services.common;\r
+\r
+import java.util.Map;\r
+\r
+public interface ResourceMap extends Map<String, ResourceBase> {\r
+\r
+}\r
--- /dev/null
+package org.collectionspace.services.common;\r
+\r
+public interface ResourceMapHolder {\r
+ public ResourceMap getResourceMap();\r
+}\r
--- /dev/null
+package org.collectionspace.services.common;\r
+\r
+import java.util.HashMap;\r
+\r
+public class ResourceMapImpl extends HashMap<String, ResourceBase> implements ResourceMap {\r
+\r
+}\r
--- /dev/null
+/**
+ * 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.invocable;
+
+import org.collectionspace.services.common.invocable.InvocationContext;
+import java.util.List;
+
+
+/**
+ * Invocation defines an interface for invocable jobs (batch, reports, exports, etc)
+ *
+ * $LastChangedRevision: $
+ * $LastChangedDate: $
+ */
+public interface Invocable {
+
+ public String INVOCATION_MODE_SINGLE = "single";
+ public String INVOCATION_MODE_GROUP = "group";
+ public String INVOCATION_MODE_LIST = "list";
+ //public String INVOCATION_MODE_QUERY = "query"; NYI
+
+ public final int STATUS_ERROR = -1;
+ public final int STATUS_UNSTARTED = 0;
+ public final int STATUS_MIN_PROGRESS = 1;
+ public final int STATUS_COMPLETE = 100;
+
+ /**
+ * @return a set of modes that this plugin can support on invocation. Must be non-empty.
+ */
+ public List<String> getSupportedInvocationModes();
+
+ /**
+ * Sets the invocation context for the batch job. Called before run().
+ * @param context an instance of InvocationContext.
+ */
+ public void setInvocationContext(InvocationContext context);
+
+ /**
+ * The main work logic of the batch job. Will be called after setContext.
+ */
+ public void run();
+
+ /**
+ * @return one of the STATUS_* constants, or a value from 1-99 to indicate progress.
+ * Implementations need not support partial completion (progress) values, and can transition
+ * from STATUS_MIN_PROGRESS to STATUS_COMPLETE.
+ */
+ public int getCompletionStatus();
+
+ /**
+ * @return information about the batch job actions and results
+ */
+ public InvocationResults getResults();
+
+ /**
+ * @return a user-presentable note when an error occurs in batch processing. Will only
+ * be called if getCompletionStatus() returns STATUS_ERROR.
+ */
+ public String getErrorInfo();
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ Invocation Context schema (XSD)
+
+ Entity : Invocation
+ Used for: JAXB binding between XML and Java objects
+
+ $LastChangedRevision: 2316 $
+ $LastChangedDate: 2010-06-02 16:03:51 -0700 (Wed, 02 Jun 2010) $
+-->
+<xs:schema
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
+ jaxb:version="1.0" elementFormDefault="unqualified"
+ xmlns:ns="http://collectionspace.org/services/common/invocable"
+ xmlns="http://collectionspace.org/services/common/invocable"
+ targetNamespace="http://collectionspace.org/services/common/invocable" version="0.1">
+
+ <xs:element name="invocationContext">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="mode" type="xs:string"/>
+ <xs:element name="docType" type="xs:string"/>
+ <xs:element name="singleCSID" type="xs:string"/>
+ <xs:element name="groupCSID" type="xs:string"/>
+ <xs:element name="listCSIDs">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="csids" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ Invocation Results schema (XSD)
+
+ Entity : Invocation
+ Used for: JAXB binding between XML and Java objects
+
+ $LastChangedRevision: 2316 $
+ $LastChangedDate: 2010-06-02 16:03:51 -0700 (Wed, 02 Jun 2010) $
+-->
+<xs:schema
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
+ jaxb:version="1.0" elementFormDefault="unqualified"
+ xmlns:ns="http://collectionspace.org/services/common/invocable"
+ xmlns="http://collectionspace.org/services/common/invocable"
+ targetNamespace="http://collectionspace.org/services/common/invocable" version="0.1">
+
+ <xs:element name="invocationResults">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="primaryURICreated" type="xs:string"/>
+ <xs:element name="userNote" type="xs:string"/>
+ <xs:element name="numAffected" type="xs:unsignedInt"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
+