From 5fd74f12a6d60cfda3d1190abb43f6709e309bdf Mon Sep 17 00:00:00 2001 From: Sanjay Dalal Date: Fri, 29 Jan 2010 22:55:28 +0000 Subject: [PATCH] CSPACE-396, CSPACE-787, CSPACE-400 Added validation handler. One or more handlers could configured in service binding. If a handler is configured for the service, the service layer callsback on the handler before create and update operations. Example validatorHandlers could be found in Account and CollectionObject services. Sends 400 (bad request) if validation fails. Deleted DocumentHandlerFactory.java and moved that code to AbstractServiceContext.java. Service context (that is aware of ther service binding) also acts as a factory for various handlers (validator, document) configured in the binding. tests: account validation (userid, tenant info), collectionobject (othernumber), all service tests M services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java A services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/nuxeo/CollectionObjectValidatorHandler.java M services/collectionobject/client/src/test/java/org/collectionspace/services/client/test/CollectionObjectServiceTest.java M services/dimension/service/src/main/java/org/collectionspace/services/dimension/DimensionResource.java M services/common/src/main/java/org/collectionspace/services/common/ServiceException.java M services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java M services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java M services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java M services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java M services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java M services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandler.java A services/common/src/main/java/org/collectionspace/services/common/document/ValidatorHandler.java D services/common/src/main/java/org/collectionspace/services/common/document/DocumentHandlerFactory.java A services/common/src/main/java/org/collectionspace/services/common/document/InvalidDocumentException.java M services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClient.java M services/common/src/main/config/tenant-bindings.xml M services/common/src/main/resources/service.xsd M services/acquisition/service/src/main/java/org/collectionspace/services/acquisition/AcquisitionResource.java M services/contact/service/src/main/java/org/collectionspace/services/contact/ContactResource.java A services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountValidatorHandler.java M services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java M services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountJpaFilter.java M services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java M services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java M services/organization/service/src/main/java/org/collectionspace/services/organization/OrgAuthorityResource.java _M services/organization/import M services/person/service/src/main/java/org/collectionspace/services/person/PersonAuthorityResource.java M services/vocabulary/service/src/main/java/org/collectionspace/services/vocabulary/VocabularyResource.java M services/intake/service/src/main/java/org/collectionspace/services/intake/IntakeResource.java M services/relation/service/src/main/java/org/collectionspace/services/relation/NewRelationResource.java --- .../client/test/AccountServiceTest.java | 2 +- .../services/account/AccountResource.java | 5 +- .../storage/AccountDocumentHandler.java | 17 --- .../account/storage/AccountJpaFilter.java | 2 +- .../storage/AccountValidatorHandler.java | 109 +++++++++++++++ .../acquisition/AcquisitionResource.java | 5 +- .../test/CollectionObjectServiceTest.java | 131 +++++++++++++++--- .../CollectionObjectResource.java | 53 ++++--- .../CollectionObjectValidatorHandler.java | 101 ++++++++++++++ .../src/main/config/tenant-bindings.xml | 6 + .../services/common/ServiceException.java | 12 +- .../context/AbstractServiceContext.java | 68 +++++++-- .../context/MultipartServiceContext.java | 18 ++- .../context/MultipartServiceContextImpl.java | 71 +++++++--- .../context/RemoteServiceContextImpl.java | 21 +-- .../common/context/ServiceContext.java | 25 +++- .../document/AbstractDocumentHandler.java | 10 ++ .../document/DocumentHandlerFactory.java | 58 -------- .../document/InvalidDocumentException.java | 88 ++++++++++++ .../common/document/ValidatorHandler.java | 46 ++++++ .../client/java/RepositoryJavaClient.java | 36 ++--- .../common/src/main/resources/service.xsd | 3 - .../services/contact/ContactResource.java | 5 +- .../services/dimension/DimensionResource.java | 5 +- .../services/intake/IntakeResource.java | 5 +- .../organization/OrgAuthorityResource.java | 9 +- .../person/PersonAuthorityResource.java | 9 +- .../relation/NewRelationResource.java | 5 +- .../vocabulary/VocabularyResource.java | 9 +- 29 files changed, 687 insertions(+), 247 deletions(-) create mode 100644 services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountValidatorHandler.java create mode 100644 services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/nuxeo/CollectionObjectValidatorHandler.java delete mode 100644 services/common/src/main/java/org/collectionspace/services/common/document/DocumentHandlerFactory.java create mode 100644 services/common/src/main/java/org/collectionspace/services/common/document/InvalidDocumentException.java create mode 100644 services/common/src/main/java/org/collectionspace/services/common/document/ValidatorHandler.java diff --git a/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java b/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java index 09bb984ad..aefc914ec 100644 --- a/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java +++ b/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java @@ -176,7 +176,7 @@ public class AccountServiceTest extends AbstractServiceTest { resource2Id = extractId(res); AccountsCommon account3 = - createAccountInstance("dj", "hithere10", "dj@dinoland.com", true, true, true); + createAccountInstance("mj", "hithere10", "mj@dinoland.com", true, true, true); res = client.create(account3); statusCode = res.getStatus(); Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java b/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java index 4cbf0f54a..2ff00573e 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java @@ -46,7 +46,6 @@ import org.collectionspace.services.common.document.BadRequestException; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.storage.StorageClient; import org.jboss.resteasy.util.HttpResponseCodes; @@ -91,9 +90,7 @@ public class AccountResource @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); docHandler.setCommonPart(ctx.getInput()); return docHandler; } diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java index ec11b7dd9..f35de7731 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java @@ -51,29 +51,12 @@ public class AccountDocumentHandler public void handleCreate(DocumentWrapper wrapDoc) throws Exception { String id = UUID.randomUUID().toString(); AccountsCommon account = wrapDoc.getWrappedObject(); - if (account.getUserId() == null || "".equals(account.getUserId())) { - String msg = "userId is missing"; - logger.error(msg); - throw new BadRequestException(msg); - } - List tl = account.getTenant(); - if (tl == null || tl.size() == 0) { - String msg = "missing tenant information!"; - logger.error(msg); - throw new BadRequestException(msg); - } account.setCsid(id); account.setStatus(Status.ACTIVE); } @Override public void handleUpdate(DocumentWrapper wrapDoc) throws Exception { - if (account.getPassword() != null - && (account.getUserId() == null || "".equals(account.getUserId()))) { - String msg = "userId is missing"; - logger.error(msg); - throw new BadRequestException(msg); - } } @Override diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountJpaFilter.java b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountJpaFilter.java index 17635853c..ad6c95353 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountJpaFilter.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountJpaFilter.java @@ -104,7 +104,7 @@ public class AccountJpaFilter extends JpaDocumentFilter { hasWhere = true; } - String email = null; + String email = null; List emailvals = getQueryParam(AccountStorageConstants.Q_EMAIL); if (emailvals != null) { email = emailvals.get(0); diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountValidatorHandler.java b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountValidatorHandler.java new file mode 100644 index 000000000..487175f15 --- /dev/null +++ b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountValidatorHandler.java @@ -0,0 +1,109 @@ +/** + * 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. + *//** + * 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. + */ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.collectionspace.services.account.storage; + +import java.util.List; +import org.collectionspace.services.account.AccountsCommon; +import org.collectionspace.services.common.context.ServiceContext; +import org.collectionspace.services.common.document.DocumentHandler.Action; +import org.collectionspace.services.common.document.InvalidDocumentException; +import org.collectionspace.services.common.document.ValidatorHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author + */ +public class AccountValidatorHandler implements ValidatorHandler { + + final Logger logger = LoggerFactory.getLogger(AccountValidatorHandler.class); + + @Override + public void validate(Action action, ServiceContext ctx) + throws InvalidDocumentException { + if (logger.isDebugEnabled()) { + logger.debug("validate() action=" + action.name()); + } + try { + AccountsCommon account = (AccountsCommon) ctx.getInput(); + String msg = "validate() "; + boolean invalid = false; + + List tl = account.getTenant(); + if (tl == null || tl.size() == 0) { + msg += " missing tenant information!"; + invalid = true; + } + if (action.equals(Action.CREATE)) { + //create specific validation here + if (account.getUserId() == null || "".equals(account.getUserId())) { + invalid = true; + msg += " userId is missing"; + } + } else if (action.equals(Action.UPDATE)) { + //update specific validation here + if (account.getPassword() != null + && (account.getUserId() == null || "".equals(account.getUserId()))) { + invalid = true; + msg += " userId is needed with password"; + } + } + if (invalid) { + logger.error(msg); + throw new InvalidDocumentException(msg); + } + } catch (InvalidDocumentException ide) { + throw ide; + } catch (Exception e) { + throw new InvalidDocumentException(e); + } + } +} diff --git a/services/acquisition/service/src/main/java/org/collectionspace/services/acquisition/AcquisitionResource.java b/services/acquisition/service/src/main/java/org/collectionspace/services/acquisition/AcquisitionResource.java index eaab12852..951146ea0 100644 --- a/services/acquisition/service/src/main/java/org/collectionspace/services/acquisition/AcquisitionResource.java +++ b/services/acquisition/service/src/main/java/org/collectionspace/services/acquisition/AcquisitionResource.java @@ -45,7 +45,6 @@ import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.query.IQueryManager; import org.collectionspace.services.common.query.QueryManager; import org.collectionspace.services.common.security.UnauthorizedException; @@ -78,9 +77,7 @@ public class AcquisitionResource @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), AcquisitionsCommon.class); if (obj != null) { diff --git a/services/collectionobject/client/src/test/java/org/collectionspace/services/client/test/CollectionObjectServiceTest.java b/services/collectionobject/client/src/test/java/org/collectionspace/services/client/test/CollectionObjectServiceTest.java index bef77e5b3..45362fa0d 100644 --- a/services/collectionobject/client/src/test/java/org/collectionspace/services/client/test/CollectionObjectServiceTest.java +++ b/services/collectionobject/client/src/test/java/org/collectionspace/services/client/test/CollectionObjectServiceTest.java @@ -104,7 +104,7 @@ public class CollectionObjectServiceTest extends AbstractServiceTest { // Store the ID returned from the first resource created // for additional tests below. - if (knownResourceId == null){ + if (knownResourceId == null) { knownResourceId = extractId(res); if (logger.isDebugEnabled()) { logger.debug(testName + ": knownResourceId=" + knownResourceId); @@ -132,14 +132,34 @@ public class CollectionObjectServiceTest extends AbstractServiceTest { // Placeholders until the three tests below can be uncommented. // See Issue CSPACE-401. @Override + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class) public void createWithEmptyEntityBody(String testName) throws Exception { } @Override + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class) public void createWithMalformedXml(String testName) throws Exception { + setupCreate(testName); + + CollectionobjectsCommon collectionObject = new CollectionobjectsCommon(); + collectionObject.setTitle("atitle"); + //don't set objectNumber to check validation + collectionObject.setObjectName("some name"); + MultipartOutput multipart = + createCollectionObjectInstance(client.getCommonPartName(), collectionObject, null); + ClientResponse res = client.create(multipart); + int statusCode = res.getStatus(); + + if (logger.isDebugEnabled()) { + logger.debug(testName + ": status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + Assert.assertEquals(statusCode, Response.Status.BAD_REQUEST.getStatusCode()); } @Override + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class) public void createWithWrongXmlSchema(String testName) throws Exception { } @@ -350,12 +370,7 @@ public class CollectionObjectServiceTest extends AbstractServiceTest { // Perform setup. setupUpdate(testName); - ClientResponse res = - client.read(knownResourceId); - if (logger.isDebugEnabled()) { - logger.debug(testName + ": read status = " + res.getStatus()); - } - Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE); + ClientResponse res = updateRetrieve(testName, knownResourceId); if (logger.isDebugEnabled()) { logger.debug("got object to update with ID: " + knownResourceId); @@ -375,12 +390,8 @@ public class CollectionObjectServiceTest extends AbstractServiceTest { CollectionobjectsCommon.class)); } - // Submit the request to the service and store the response. - MultipartOutput output = new MultipartOutput(); - OutputPart commonPart = output.addPart(collectionObject, MediaType.APPLICATION_XML_TYPE); - commonPart.getHeaders().add("label", client.getCommonPartName()); + res = updateSend(testName, knownResourceId, collectionObject); - res = client.update(knownResourceId, output); int statusCode = res.getStatus(); // Check the status code of the response: does it match the expected response(s)? if (logger.isDebugEnabled()) { @@ -403,18 +414,82 @@ public class CollectionObjectServiceTest extends AbstractServiceTest { } + private ClientResponse updateRetrieve(String testName, String id) { + ClientResponse res = + client.read(id); + if (logger.isDebugEnabled()) { + logger.debug("read in updateRetrieve for " + testName + " status = " + res.getStatus()); + } + Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE); + + if (logger.isDebugEnabled()) { + logger.debug("got object to updateRetrieve for " + testName + " with ID: " + id); + } + return res; + } + + private ClientResponse updateSend(String testName, String id, + CollectionobjectsCommon collectionObject) { + MultipartOutput output = new MultipartOutput(); + OutputPart commonPart = output.addPart(collectionObject, MediaType.APPLICATION_XML_TYPE); + commonPart.getHeaders().add("label", client.getCommonPartName()); + + ClientResponse res = client.update(knownResourceId, output); + // Check the status code of the response: does it match the expected response(s)? + if (logger.isDebugEnabled()) { + logger.debug("updateSend for " + testName + ": status = " + res.getStatus()); + } + return res; + } + // Failure outcomes // Placeholders until the three tests below can be uncommented. // See Issue CSPACE-401. @Override + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"read"}) public void updateWithEmptyEntityBody(String testName) throws Exception { } @Override + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"read"}) public void updateWithMalformedXml(String testName) throws Exception { + // Perform setup. + setupUpdate(testName); + if (logger.isDebugEnabled()) { + logger.debug(testName + " got object to update with ID: " + knownResourceId); + } + + ClientResponse res = updateRetrieve(testName, knownResourceId); + + MultipartInput input = (MultipartInput) res.getEntity(); + CollectionobjectsCommon collectionObject = + (CollectionobjectsCommon) extractPart(input, + client.getCommonPartName(), CollectionobjectsCommon.class); + Assert.assertNotNull(collectionObject); + + //update with invalid content + collectionObject.setObjectNumber(""); + + if (logger.isDebugEnabled()) { + logger.debug(testName + " updated object"); + logger.debug(objectAsXmlString(collectionObject, + CollectionobjectsCommon.class)); + } + + // Submit the request to the service and store the response. + res = updateSend(testName, knownResourceId, collectionObject); + int statusCode = res.getStatus(); + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + Assert.assertEquals(statusCode, Response.Status.BAD_REQUEST.getStatusCode()); + } @Override + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"read"}) public void updateWithWrongXmlSchema(String testName) throws Exception { } @@ -654,6 +729,20 @@ public class CollectionObjectServiceTest extends AbstractServiceTest { collectionObject.setBriefDescription("Papier mache bird cow mask with horns, " + "painted red with black and yellow spots. " + "Puerto Rico. ca. 8" high, 6" wide, projects 10" (with horns)."); + + CollectionobjectsNaturalhistory conh = new CollectionobjectsNaturalhistory(); + conh.setNhString("test-string"); + conh.setNhInt(999); + conh.setNhLong(9999); + + + MultipartOutput multipart = createCollectionObjectInstance(commonPartName, collectionObject, conh); + return multipart; + } + + private MultipartOutput createCollectionObjectInstance(String commonPartName, + CollectionobjectsCommon collectionObject, CollectionobjectsNaturalhistory conh) { + MultipartOutput multipart = new MultipartOutput(); OutputPart commonPart = multipart.addPart(collectionObject, MediaType.APPLICATION_XML_TYPE); @@ -665,17 +754,15 @@ public class CollectionObjectServiceTest extends AbstractServiceTest { CollectionobjectsCommon.class)); } - CollectionobjectsNaturalhistory conh = new CollectionobjectsNaturalhistory(); - conh.setNhString("test-string"); - conh.setNhInt(999); - conh.setNhLong(9999); - OutputPart nhPart = multipart.addPart(conh, MediaType.APPLICATION_XML_TYPE); - nhPart.getHeaders().add("label", getNHPartName()); + if (conh != null) { + OutputPart nhPart = multipart.addPart(conh, MediaType.APPLICATION_XML_TYPE); + nhPart.getHeaders().add("label", getNHPartName()); - if (logger.isDebugEnabled()) { - logger.debug("to be created, collectionobject nhistory"); - logger.debug(objectAsXmlString(conh, - CollectionobjectsNaturalhistory.class)); + if (logger.isDebugEnabled()) { + logger.debug("to be created, collectionobject nhistory"); + logger.debug(objectAsXmlString(conh, + CollectionobjectsNaturalhistory.class)); + } } return multipart; diff --git a/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java b/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java index caf3e1c69..44e9ae78a 100644 --- a/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java +++ b/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java @@ -46,10 +46,10 @@ import org.collectionspace.services.common.AbstractCollectionSpaceResource; import org.collectionspace.services.common.context.MultipartServiceContext; import org.collectionspace.services.common.context.MultipartServiceContextFactory; import org.collectionspace.services.common.context.ServiceContext; +import org.collectionspace.services.common.document.BadRequestException; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler; import org.collectionspace.services.common.document.DocumentFilter; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.security.UnauthorizedException; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; @@ -68,11 +68,11 @@ public class CollectionObjectResource @Override protected String getVersionString() { - /** The last change revision. */ - final String lastChangeRevision = "$LastChangedRevision$"; - return lastChangeRevision; + /** The last change revision. */ + final String lastChangeRevision = "$LastChangedRevision$"; + return lastChangeRevision; } - + @Override public String getServiceName() { return serviceName; @@ -80,11 +80,10 @@ public class CollectionObjectResource @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { - Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), CollectionobjectsCommon.class); + Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), + CollectionobjectsCommon.class); if (obj != null) { docHandler.setCommonPart((CollectionobjectsCommon) obj); } @@ -102,6 +101,10 @@ public class CollectionObjectResource 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(); @@ -211,6 +214,10 @@ public class CollectionObjectResource DocumentHandler handler = createDocumentHandler(ctx); getRepositoryClient(ctx).update(ctx, csid, handler); result = (MultipartOutput) ctx.getOutput(); + } catch (BadRequestException bre) { + Response response = Response.status( + Response.Status.BAD_REQUEST).entity("Update 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(); @@ -268,12 +275,12 @@ public class CollectionObjectResource } } - + @GET - @Path("/search") + @Path("/search") @Produces("application/xml") public CollectionobjectsCommonList keywordsSearchCollectionObjects(@Context UriInfo ui, - @QueryParam (IQueryManager.SEARCH_TYPE_KEYWORDS) String keywords) { + @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS) String keywords) { CollectionobjectsCommonList collectionObjectList = new CollectionobjectsCommonList(); try { ServiceContext ctx = MultipartServiceContextFactory.get().createServiceContext(null, getServiceName()); @@ -281,18 +288,18 @@ public class CollectionObjectResource // perform a keyword search if (keywords != null && !keywords.isEmpty()) { - String whereClause = QueryManager.createWhereClauseFromKeywords(keywords); - DocumentFilter documentFilter = handler.getDocumentFilter(); - documentFilter.setWhereClause(whereClause); - if (logger.isDebugEnabled()) { - logger.debug("The WHERE clause is: " + documentFilter.getWhereClause()); - } - getRepositoryClient(ctx).getFiltered(ctx, handler); + String whereClause = QueryManager.createWhereClauseFromKeywords(keywords); + DocumentFilter documentFilter = handler.getDocumentFilter(); + documentFilter.setWhereClause(whereClause); + if (logger.isDebugEnabled()) { + logger.debug("The WHERE clause is: " + documentFilter.getWhereClause()); + } + getRepositoryClient(ctx).getFiltered(ctx, handler); } else { - getRepositoryClient(ctx).getAll(ctx, handler); - } + getRepositoryClient(ctx).getAll(ctx, handler); + } collectionObjectList = (CollectionobjectsCommonList) handler.getCommonPartList(); - + } catch (UnauthorizedException ue) { Response response = Response.status( Response.Status.UNAUTHORIZED).entity("Index failed reason " + ue.getErrorReason()).type("text/plain").build(); @@ -306,5 +313,5 @@ public class CollectionObjectResource throw new WebApplicationException(response); } return collectionObjectList; - } + } } diff --git a/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/nuxeo/CollectionObjectValidatorHandler.java b/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/nuxeo/CollectionObjectValidatorHandler.java new file mode 100644 index 000000000..a8a8730ea --- /dev/null +++ b/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/nuxeo/CollectionObjectValidatorHandler.java @@ -0,0 +1,101 @@ +/** + * 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. + *//** + * 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. + */ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.collectionspace.services.collectionobject.nuxeo; + +import org.collectionspace.services.collectionobject.CollectionobjectsCommon; +import org.collectionspace.services.common.context.MultipartServiceContext; +import org.collectionspace.services.common.context.ServiceContext; +import org.collectionspace.services.common.document.DocumentHandler.Action; +import org.collectionspace.services.common.document.InvalidDocumentException; +import org.collectionspace.services.common.document.ValidatorHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author + */ +public class CollectionObjectValidatorHandler implements ValidatorHandler { + + final Logger logger = LoggerFactory.getLogger(CollectionObjectValidatorHandler.class); + + @Override + public void validate(Action action, ServiceContext ctx) + throws InvalidDocumentException { + if(logger.isDebugEnabled()) { + logger.debug("validate() action=" + action.name()); + } + try { + MultipartServiceContext mctx = (MultipartServiceContext) ctx; + CollectionobjectsCommon co = (CollectionobjectsCommon) mctx.getInputPart(mctx.getCommonPartLabel(), + CollectionobjectsCommon.class); + String msg = ""; + boolean invalid = false; + if (co.getObjectNumber() == null || co.getObjectNumber().isEmpty()) { + invalid = true; + msg += "objectNumber is missing!"; + } + if(action.equals(Action.CREATE)) { + //create specific validation here + } else if(action.equals(Action.UPDATE)) { + //update specific validation here + } + + if (invalid) { + logger.error(msg); + throw new InvalidDocumentException(msg); + } + } catch (InvalidDocumentException ide) { + throw ide; + } catch (Exception e) { + throw new InvalidDocumentException(e); + } + } +} diff --git a/services/common/src/main/config/tenant-bindings.xml b/services/common/src/main/config/tenant-bindings.xml index 4c3b3ae69..072bfdb39 100644 --- a/services/common/src/main/config/tenant-bindings.xml +++ b/services/common/src/main/config/tenant-bindings.xml @@ -19,6 +19,9 @@ org.collectionspace.services.collectionobject.nuxeo.CollectionObjectDocumentModelHandler + + org.collectionspace.services.collectionobject.nuxeo.CollectionObjectValidatorHandler + org.collectionspace.services.account.storage.AccountDocumentHandler + + org.collectionspace.services.account.storage.AccountValidatorHandler + ServiceException without detail message, error code @@ -68,8 +67,8 @@ public class ServiceException extends Exception { * @param errorReason reason for error */ public ServiceException(int errorCode, String errorReason) { + super(errorReason); this.errorCode = errorCode; - this.errorReason = errorReason; } /** @@ -124,13 +123,6 @@ public class ServiceException extends Exception { * @return the errorReason */ public String getErrorReason() { - return errorReason; - } - - /** - * @param errorReason the ErrorReason to set - */ - public void setErrorReason(String errorReason) { - this.errorReason = errorReason; + return getMessage(); } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java index 7c4351581..0bdca628f 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java @@ -24,6 +24,7 @@ package org.collectionspace.services.common.context; import java.security.acl.Group; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; @@ -37,6 +38,8 @@ import org.collectionspace.authentication.CSpaceTenant; import org.collectionspace.services.common.ClientType; import org.collectionspace.services.common.ServiceMain; import org.collectionspace.services.common.config.TenantBindingConfigReader; +import org.collectionspace.services.common.document.DocumentHandler; +import org.collectionspace.services.common.document.ValidatorHandler; import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.service.ObjectPartType; import org.collectionspace.services.common.service.ServiceBindingType; @@ -60,6 +63,8 @@ public abstract class AbstractServiceContext private ServiceBindingType serviceBinding; private TenantBindingType tenantBinding; private String overrideDocumentType = null; + private List valHandlers = null; + private DocumentHandler docHandler = null; public AbstractServiceContext(String serviceName) throws UnauthorizedException { TenantBindingConfigReader tReader = @@ -130,7 +135,7 @@ public abstract class AbstractServiceContext @Override public String getRepositoryClientName() { - if(serviceBinding.getRepositoryClient() == null) { + if (serviceBinding.getRepositoryClient() == null) { return null; } return serviceBinding.getRepositoryClient().trim(); @@ -164,19 +169,6 @@ public abstract class AbstractServiceContext return serviceBinding; } - @Override - public String getDocumentHandlerClass() { - if (serviceBinding.getDocumentHandler() == null - || serviceBinding.getDocumentHandler().isEmpty()) { - String msg = "Missing documentHandler in service binding for " - + getServiceName() + " for tenant id=" + getTenantId() - + " name=" + getTenantName(); - logger.error(msg); - throw new IllegalStateException(msg); - } - return serviceBinding.getDocumentHandler().trim(); - } - @Override public String getServiceName() { return serviceBinding.getName(); @@ -282,6 +274,54 @@ public abstract class AbstractServiceContext return tenantId; } + @Override + public DocumentHandler getDocumentHandler() throws Exception { + if (docHandler != null) { + return docHandler; + } + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + Class c = tccl.loadClass(getDocumentHandlerClass()); + if (DocumentHandler.class.isAssignableFrom(c)) { + docHandler = (DocumentHandler) c.newInstance(); + } else { + throw new IllegalArgumentException("Not of type " + + DocumentHandler.class.getCanonicalName()); + } + docHandler.setServiceContext(this); + return docHandler; + } + + private String getDocumentHandlerClass() { + if (serviceBinding.getDocumentHandler() == null + || serviceBinding.getDocumentHandler().isEmpty()) { + String msg = "Missing documentHandler in service binding for " + + getServiceName() + " for tenant id=" + getTenantId() + + " name=" + getTenantName(); + logger.error(msg); + throw new IllegalStateException(msg); + } + return serviceBinding.getDocumentHandler().trim(); + } + + @Override + public List getValidatorHandlers() throws Exception { + if (valHandlers != null) { + return valHandlers; + } + List handlerClazzes = getServiceBinding().getValidatorHandler(); + List handlers = new ArrayList(handlerClazzes.size()); + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + for (String clazz : handlerClazzes) { + clazz = clazz.trim(); + Class c = tccl.loadClass(clazz); + if (ValidatorHandler.class.isAssignableFrom(c)) { + handlers.add((ValidatorHandler) c.newInstance()); + } + } + valHandlers = handlers; + return valHandlers; + } + @Override public String toString() { StringBuilder msg = new StringBuilder(); diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java index e17a5abdc..7063485a1 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContext.java @@ -24,6 +24,7 @@ package org.collectionspace.services.common.context; import java.io.IOException; +import java.io.InputStream; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; import org.w3c.dom.Document; @@ -65,13 +66,28 @@ public interface MultipartServiceContext public void setOutput(MultipartOutput output); /** - * getInputPart returns part for given label from input + * getInputPart returns the input part object for given label and clazz * @param label * @param clazz class of the object * @return part */ public Object getInputPart(String label, Class clazz) throws IOException; + /** + * getInputPartAsString returns the input part with given label in the string form + * @param label + * @return + * @throws IOException + */ + public String getInputPartAsString(String label) throws IOException; + + /** + * getInputPartAsStream returns input part as stream for given label + * @param label + * @return + * @throws IOException + */ + public InputStream getInputPartAsStream(String label) throws IOException; /** * addOutputPart adds given XML part with given label and content type to output * @param label diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java index 5e99ab850..a997a4102 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/MultipartServiceContextImpl.java @@ -23,8 +23,10 @@ */ package org.collectionspace.services.common.context; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Constructor; import javax.ws.rs.core.MediaType; import org.collectionspace.services.common.document.DocumentUtils; @@ -55,41 +57,68 @@ public class MultipartServiceContextImpl } - @Override - public Object getInputPart(String label, Class clazz) throws IOException { - Object obj = null; - if(getInput() != null){ + private InputPart getInputPart(String label) throws IOException { + if (getInput() != null) { MultipartInput fdip = getInput(); - for(InputPart part : fdip.getParts()){ + for (InputPart part : fdip.getParts()) { String partLabel = part.getHeaders().getFirst("label"); - if(label.equalsIgnoreCase(partLabel)){ - if(logger.isDebugEnabled()){ - logger.debug("received part label=" + partLabel + - "\npayload=" + part.getBodyAsString()); + if (label.equalsIgnoreCase(partLabel)) { + if (logger.isTraceEnabled()) { + logger.trace("getInputPart found part with label=" + partLabel + + "\npayload=" + part.getBodyAsString()); } - obj = part.getBody(clazz, null); - break; + return part; } } } + return null; + } + + @Override + public Object getInputPart(String label, Class clazz) throws IOException { + Object obj = null; + InputPart part = getInputPart(label); + if (part != null) { + obj = part.getBody(clazz, null); + } return obj; } + @Override + public String getInputPartAsString(String label) throws IOException { + InputPart part = getInputPart(label); + if (part != null) { + return part.getBodyAsString(); + } + return null; + } + + @Override + public InputStream getInputPartAsStream(String label) throws IOException { + InputPart part = getInputPart(label); + if (part != null) { + return new ByteArrayInputStream(part.getBodyAsString().getBytes()); + } + return null; + } + + + @Override public void addOutputPart(String label, Document doc, String contentType) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try{ + try { DocumentUtils.writeDocument(doc, baos); baos.close(); OutputPart part = getOutput().addPart(new String(baos.toByteArray()), MediaType.valueOf(contentType)); part.getHeaders().add("label", label); - }finally{ - if(baos != null){ - try{ + } finally { + if (baos != null) { + try { baos.close(); - }catch(Exception e){ + } catch (Exception e) { } } } @@ -99,13 +128,13 @@ public class MultipartServiceContextImpl public ServiceContext getLocalContext(String localContextClassName) throws Exception { ClassLoader cloader = Thread.currentThread().getContextClassLoader(); Class ctxClass = cloader.loadClass(localContextClassName); - if(!ServiceContext.class.isAssignableFrom(ctxClass)) { - throw new IllegalArgumentException("getLocalContext requires " + - " implementation of " + ServiceContext.class.getName()); + if (!ServiceContext.class.isAssignableFrom(ctxClass)) { + throw new IllegalArgumentException("getLocalContext requires " + + " implementation of " + ServiceContext.class.getName()); } - + Constructor ctor = ctxClass.getConstructor(java.lang.String.class); - ServiceContext ctx = (ServiceContext)ctor.newInstance(getServiceName()); + ServiceContext ctx = (ServiceContext) ctor.newInstance(getServiceName()); return ctx; } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java b/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java index d53218644..ca4da8099 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/RemoteServiceContextImpl.java @@ -53,7 +53,13 @@ public class RemoteServiceContextImpl } @Override - public void setInput(IT input){ + public void setInput(IT input) { + //for security reasons, do not allow to set input again (from handlers) + if (this.input != null) { + String msg = "Non-null input cannot be set!"; + logger.error(msg); + throw new IllegalStateException(msg); + } this.input = input; } @@ -63,22 +69,21 @@ public class RemoteServiceContextImpl } @Override - public void setOutput(OT output){ + public void setOutput(OT output) { this.output = output; } - @Override public ServiceContext getLocalContext(String localContextClassName) throws Exception { ClassLoader cloader = Thread.currentThread().getContextClassLoader(); Class ctxClass = cloader.loadClass(localContextClassName); - if(!ServiceContext.class.isAssignableFrom(ctxClass)) { - throw new IllegalArgumentException("getLocalContext requires " + - " implementation of " + ServiceContext.class.getName()); + if (!ServiceContext.class.isAssignableFrom(ctxClass)) { + throw new IllegalArgumentException("getLocalContext requires " + + " implementation of " + ServiceContext.class.getName()); } - + Constructor ctor = ctxClass.getConstructor(java.lang.String.class); - ServiceContext ctx = (ServiceContext)ctor.newInstance(getServiceName()); + ServiceContext ctx = (ServiceContext) ctor.newInstance(getServiceName()); return ctx; } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java index 6ce74c0be..ac06bd2f4 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java @@ -23,10 +23,11 @@ */ package org.collectionspace.services.common.context; -import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Properties; import org.collectionspace.services.common.ClientType; +import org.collectionspace.services.common.document.DocumentHandler; +import org.collectionspace.services.common.document.ValidatorHandler; import org.collectionspace.services.common.service.ObjectPartType; import org.collectionspace.services.common.service.ServiceBindingType; @@ -126,11 +127,6 @@ public interface ServiceContext { */ public String getRepositoryWorkspaceId(); - /** - * getDocumentHandlerClass returns the class name for document handler - * @return class name of document handler - */ - public String getDocumentHandlerClass(); /** * Get input parts as received over the wire from service consumer @@ -200,6 +196,21 @@ public interface ServiceContext { */ public void setProperty(String name, Object o); + + /** + * getDocumentHanlder returns document handler configured in the the binding + * it creates the handler if necessary. + * @return document handler + */ + public DocumentHandler getDocumentHandler() throws Exception; + + /** + * getValidatorHandlers returns registered (from binding) validtor handlers + * for the service. it creates the handlers if necessary. + * @return validation handlers + */ + public List getValidatorHandlers() throws Exception; + } diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandler.java b/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandler.java index 12821e812..a6d388f00 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandler.java +++ b/services/common/src/main/java/org/collectionspace/services/common/document/AbstractDocumentHandler.java @@ -24,6 +24,7 @@ package org.collectionspace.services.common.document; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.StringTokenizer; @@ -98,10 +99,12 @@ public abstract class AbstractDocumentHandler final public void prepare(Action action) throws Exception { switch (action) { case CREATE: + validate(action); prepareCreate(); break; case UPDATE: + validate(action); prepareUpdate(); break; @@ -258,4 +261,11 @@ public abstract class AbstractDocumentHandler public String getServiceContextPath() { return "/" + getServiceContext().getServiceName().toLowerCase() + "/"; } + + private void validate(Action action) throws Exception { + List valHandlers = serviceContext.getValidatorHandlers(); + for (ValidatorHandler handler : valHandlers) { + handler.validate(action, serviceContext); + } + } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/DocumentHandlerFactory.java b/services/common/src/main/java/org/collectionspace/services/common/document/DocumentHandlerFactory.java deleted file mode 100644 index 3bd6d7584..000000000 --- a/services/common/src/main/java/org/collectionspace/services/common/document/DocumentHandlerFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * 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.document; - -/** - * - * DocumentHandlerFactory creates document handler - * - */ -public class DocumentHandlerFactory { - - private static final DocumentHandlerFactory self = new DocumentHandlerFactory(); - - private DocumentHandlerFactory() { - } - - public static DocumentHandlerFactory getInstance() { - return self; - } - - /** - * getHandler returns a document handler. The factory may create a new - * stateful handler or return an existing stateless handler. - * @param clazz name of the class to instantiate. The class should implement - * DocumentHandler - */ - public DocumentHandler getHandler(String clazz) - throws ClassNotFoundException, InstantiationException, IllegalAccessException { - ClassLoader tccl = Thread.currentThread().getContextClassLoader(); - Class c = tccl.loadClass(clazz); - if (DocumentHandler.class.isAssignableFrom(c)) { - return (DocumentHandler) c.newInstance(); - } else { - throw new IllegalArgumentException("Not of type " + DocumentHandler.class.getCanonicalName()); - } - } -} diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/InvalidDocumentException.java b/services/common/src/main/java/org/collectionspace/services/common/document/InvalidDocumentException.java new file mode 100644 index 000000000..0abded31b --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/common/document/InvalidDocumentException.java @@ -0,0 +1,88 @@ +/** + * 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. + */ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.collectionspace.services.common.document; + +/** + * InvalidDocumentException is used to indicate one or more parts of service + * document has failed to validate + * @author + */ +public class InvalidDocumentException extends BadRequestException { + + /** + * Creates a new instance of InvalidDocumentException without detail message. + */ + public InvalidDocumentException() { + } + + /** + * Constructs an instance of InvalidDocumentException with the specified detail message. + * @param msg the detail message. + */ + public InvalidDocumentException(String msg) { + super(msg); + } + + /** + * Constructs a new exception with the specified detail message and + * cause.

Note that the detail message associated with + * cause is not automatically incorporated in + * this exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public InvalidDocumentException(String message, Throwable cause) { + super(message, cause); + setErrorCode(HTTP_CODE); + } + + /** + * Constructs a new exception with the specified cause and a detail + * message of (cause==null ? null : cause.toString()) (which + * typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than + * wrappers for other throwables (for example, {@link + * java.security.PrivilegedActionException}). + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public InvalidDocumentException(Throwable cause) { + super(cause); + setErrorCode(HTTP_CODE); + } +} diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/ValidatorHandler.java b/services/common/src/main/java/org/collectionspace/services/common/document/ValidatorHandler.java new file mode 100644 index 000000000..2fc6559a7 --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/common/document/ValidatorHandler.java @@ -0,0 +1,46 @@ +/** + * 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.document; + +import org.collectionspace.services.common.context.ServiceContext; +import org.collectionspace.services.common.document.DocumentHandler.Action; + +/** + * ValidatorHandler provides plugin for application level validation + * for content received by services serving objects with extensible schema + */ +public interface ValidatorHandler { + + /** + * validate is called by the document handler for CREATE and UPDATE actions. + * validation is performed as soon as requests are unmarshalled but before any + * storage work is performed. + * the handler is caleld only if it is registered using the service binding + * @param action + * @param ctx + * @throws InvalidDocumentException + */ + public void validate(Action action, ServiceContext ctx) + throws InvalidDocumentException; +} diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClient.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClient.java index 15f9b0362..27f475394 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClient.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClient.java @@ -84,8 +84,8 @@ public class RepositoryJavaClient implements RepositoryClient { String nuxeoWspaceId = ctx.getRepositoryWorkspaceId(); if (nuxeoWspaceId == null) { throw new DocumentNotFoundException( - "Unable to find workspace for service " + ctx.getServiceName() + - " check if the workspace exists in the Nuxeo repository"); + "Unable to find workspace for service " + ctx.getServiceName() + + " check if the workspace exists in the Nuxeo repository"); } RepositoryInstance repoSession = null; try { @@ -107,6 +107,8 @@ public class RepositoryJavaClient implements RepositoryClient { repoSession.save(); handler.complete(Action.CREATE, wrapDoc); return id; + } catch (BadRequestException bre) { + throw bre; } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug("Caught exception ", e); @@ -193,9 +195,9 @@ public class RepositoryJavaClient implements RepositoryClient { String nuxeoWspaceId = ctx.getRepositoryWorkspaceId(); if (nuxeoWspaceId == null) { throw new DocumentNotFoundException( - "Unable to find workspace for service " + - ctx.getServiceName() + - " check if the workspace exists in the Nuxeo repository"); + "Unable to find workspace for service " + + ctx.getServiceName() + + " check if the workspace exists in the Nuxeo repository"); } RepositoryInstance repoSession = null; @@ -264,7 +266,7 @@ public class RepositoryJavaClient implements RepositoryClient { query.append(" WHERE ecm:path STARTSWITH '/" + domain + "'"); if ((null != where) && (where.length() > 0)) { // query.append(" AND " + where); - query.append(" AND " + where + "AND ecm:isProxy = 0"); + query.append(" AND " + where + "AND ecm:isProxy = 0"); } DocumentModelList docList = null; if ((docFilter.getOffset() > 0) || (docFilter.getPageSize() > 0)) { @@ -331,6 +333,8 @@ public class RepositoryJavaClient implements RepositoryClient { repoSession.saveDocument(doc); repoSession.save(); handler.complete(Action.UPDATE, wrapDoc); + } catch (BadRequestException bre) { + throw bre; } catch (DocumentException de) { throw de; } catch (Exception e) { @@ -392,20 +396,20 @@ public class RepositoryJavaClient implements RepositoryClient { try { repoSession = getRepositorySession(); DocumentRef docRef = new PathRef( - "/" + tenantDomain + - "/" + "workspaces"); + "/" + tenantDomain + + "/" + "workspaces"); DocumentModel parent = repoSession.getDocument(docRef); DocumentModel doc = repoSession.createDocumentModel(parent.getPathAsString(), workspaceName, "Workspace"); doc.setPropertyValue("dc:title", workspaceName); - doc.setPropertyValue("dc:description", "A CollectionSpace workspace for " + - workspaceName); + doc.setPropertyValue("dc:description", "A CollectionSpace workspace for " + + workspaceName); doc = repoSession.createDocument(doc); workspaceId = doc.getId(); repoSession.save(); if (logger.isDebugEnabled()) { - logger.debug("created workspace name=" + workspaceName + - " id=" + workspaceId); + logger.debug("created workspace name=" + workspaceName + + " id=" + workspaceId); } } catch (Exception e) { if (logger.isDebugEnabled()) { @@ -427,9 +431,9 @@ public class RepositoryJavaClient implements RepositoryClient { try { repoSession = getRepositorySession(); DocumentRef docRef = new PathRef( - "/" + tenantDomain + - "/" + "workspaces" + - "/" + workspaceName); + "/" + tenantDomain + + "/" + "workspaces" + + "/" + workspaceName); DocumentModel workspace = repoSession.getDocument(docRef); workspaceId = workspace.getId(); } catch (DocumentException de) { @@ -465,7 +469,7 @@ public class RepositoryJavaClient implements RepositoryClient { client.releaseRepository(repoSession); } catch (Exception e) { logger.error("Could not close the repository session", e); - // no need to throw this service specific exception + // no need to throw this service specific exception } } } diff --git a/services/common/src/main/resources/service.xsd b/services/common/src/main/resources/service.xsd index a944daa64..78e85f2fe 100644 --- a/services/common/src/main/resources/service.xsd +++ b/services/common/src/main/resources/service.xsd @@ -39,9 +39,6 @@ - - - diff --git a/services/contact/service/src/main/java/org/collectionspace/services/contact/ContactResource.java b/services/contact/service/src/main/java/org/collectionspace/services/contact/ContactResource.java index 6879e24d8..22fc2dad0 100644 --- a/services/contact/service/src/main/java/org/collectionspace/services/contact/ContactResource.java +++ b/services/contact/service/src/main/java/org/collectionspace/services/contact/ContactResource.java @@ -46,7 +46,6 @@ import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; @@ -82,9 +81,7 @@ public class ContactResource extends AbstractCollectionSpaceResource { @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext)ctx).getInputPart(ctx.getCommonPartLabel(), ContactsCommon.class); if (obj != null) { diff --git a/services/dimension/service/src/main/java/org/collectionspace/services/dimension/DimensionResource.java b/services/dimension/service/src/main/java/org/collectionspace/services/dimension/DimensionResource.java index 73ed3f3a9..c7d27878e 100644 --- a/services/dimension/service/src/main/java/org/collectionspace/services/dimension/DimensionResource.java +++ b/services/dimension/service/src/main/java/org/collectionspace/services/dimension/DimensionResource.java @@ -47,7 +47,6 @@ import org.collectionspace.services.common.context.MultipartServiceContextFactor import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; import org.jboss.resteasy.util.HttpResponseCodes; @@ -82,9 +81,7 @@ public class DimensionResource extends AbstractCollectionSpaceResource { @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext)ctx).getInputPart(ctx.getCommonPartLabel(), DimensionsCommon.class); if (obj != null) { diff --git a/services/intake/service/src/main/java/org/collectionspace/services/intake/IntakeResource.java b/services/intake/service/src/main/java/org/collectionspace/services/intake/IntakeResource.java index d84f4138c..d437fb002 100644 --- a/services/intake/service/src/main/java/org/collectionspace/services/intake/IntakeResource.java +++ b/services/intake/service/src/main/java/org/collectionspace/services/intake/IntakeResource.java @@ -48,7 +48,6 @@ import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.query.IQueryManager; import org.collectionspace.services.common.query.QueryManager; import org.collectionspace.services.common.security.UnauthorizedException; @@ -86,9 +85,7 @@ public class IntakeResource extends AbstractCollectionSpaceResource { @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), IntakesCommon.class); if (obj != null) { diff --git a/services/organization/service/src/main/java/org/collectionspace/services/organization/OrgAuthorityResource.java b/services/organization/service/src/main/java/org/collectionspace/services/organization/OrgAuthorityResource.java index 3826866d6..6f592b92c 100644 --- a/services/organization/service/src/main/java/org/collectionspace/services/organization/OrgAuthorityResource.java +++ b/services/organization/service/src/main/java/org/collectionspace/services/organization/OrgAuthorityResource.java @@ -48,7 +48,6 @@ import org.collectionspace.services.common.context.MultipartServiceContextFactor import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.query.IQueryManager; @@ -99,9 +98,7 @@ public class OrgAuthorityResource extends AbstractCollectionSpaceResource { */ @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler =ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), OrgauthoritiesCommon.class); if (obj != null) { @@ -114,9 +111,7 @@ public class OrgAuthorityResource extends AbstractCollectionSpaceResource { private DocumentHandler createItemDocumentHandler( ServiceContext ctx, String inAuthority) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); ((OrganizationDocumentModelHandler) docHandler).setInAuthority(inAuthority); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(getItemServiceName()), diff --git a/services/person/service/src/main/java/org/collectionspace/services/person/PersonAuthorityResource.java b/services/person/service/src/main/java/org/collectionspace/services/person/PersonAuthorityResource.java index de99b0d5f..45f9f9c23 100644 --- a/services/person/service/src/main/java/org/collectionspace/services/person/PersonAuthorityResource.java +++ b/services/person/service/src/main/java/org/collectionspace/services/person/PersonAuthorityResource.java @@ -48,7 +48,6 @@ import org.collectionspace.services.common.context.MultipartServiceContextFactor import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.query.IQueryManager; @@ -99,9 +98,7 @@ public class PersonAuthorityResource extends AbstractCollectionSpaceResource { */ @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), PersonauthoritiesCommon.class); if (obj != null) { @@ -114,9 +111,7 @@ public class PersonAuthorityResource extends AbstractCollectionSpaceResource { private DocumentHandler createItemDocumentHandler( ServiceContext ctx, String inAuthority) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); ((PersonDocumentModelHandler) docHandler).setInAuthority(inAuthority); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(getItemServiceName()), diff --git a/services/relation/service/src/main/java/org/collectionspace/services/relation/NewRelationResource.java b/services/relation/service/src/main/java/org/collectionspace/services/relation/NewRelationResource.java index c7c2d3909..6763ff0bb 100644 --- a/services/relation/service/src/main/java/org/collectionspace/services/relation/NewRelationResource.java +++ b/services/relation/service/src/main/java/org/collectionspace/services/relation/NewRelationResource.java @@ -50,7 +50,6 @@ import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.relation.IRelationsManager; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.security.UnauthorizedException; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; @@ -80,9 +79,7 @@ public class NewRelationResource extends AbstractCollectionSpaceResource { @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), RelationsCommon.class); if (obj != null) { diff --git a/services/vocabulary/service/src/main/java/org/collectionspace/services/vocabulary/VocabularyResource.java b/services/vocabulary/service/src/main/java/org/collectionspace/services/vocabulary/VocabularyResource.java index 6360a229f..d933061bd 100644 --- a/services/vocabulary/service/src/main/java/org/collectionspace/services/vocabulary/VocabularyResource.java +++ b/services/vocabulary/service/src/main/java/org/collectionspace/services/vocabulary/VocabularyResource.java @@ -48,7 +48,6 @@ import org.collectionspace.services.common.context.MultipartServiceContextFactor import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentHandlerFactory; import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.query.IQueryManager; @@ -99,9 +98,7 @@ public class VocabularyResource extends AbstractCollectionSpaceResource { */ @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(), VocabulariesCommon.class); if (obj != null) { @@ -114,9 +111,7 @@ public class VocabularyResource extends AbstractCollectionSpaceResource { private DocumentHandler createItemDocumentHandler( ServiceContext ctx, String inVocabulary) throws Exception { - DocumentHandler docHandler = DocumentHandlerFactory.getInstance().getHandler( - ctx.getDocumentHandlerClass()); - docHandler.setServiceContext(ctx); + DocumentHandler docHandler = ctx.getDocumentHandler(); ((VocabularyItemDocumentModelHandler) docHandler).setInVocabulary(inVocabulary); if (ctx.getInput() != null) { Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(getItemServiceName()), -- 2.47.3