From 07921a54ee5151a868da7c2f279db3840ac2baea Mon Sep 17 00:00:00 2001 From: Richard Millet Date: Wed, 29 Jun 2011 18:27:38 +0000 Subject: [PATCH] CSPACE-4168: Fixing import service to support mutliple tenants and tenant specific doctypes. --- .../common/ConfigurationException.java | 65 +++++++++++++++++++ .../context/AbstractServiceContextImpl.java | 2 +- .../common/context/ServiceBindingUtils.java | 5 ++ .../client/java/RepositoryJavaClientImpl.java | 4 +- .../services/nuxeo/util/NuxeoUtils.java | 17 ++++- services/imports/build.xml | 3 +- services/imports/service/pom.xml | 6 ++ .../services/imports/ImportsResource.java | 49 +++++++++++--- .../services/imports/TemplateExpander.java | 49 +++++++++----- .../resources/templates/service-document.xml | 6 +- .../services/test/ImportsServiceTest.java | 3 +- .../requests/collectionobject-request.xml | 7 -- 12 files changed, 174 insertions(+), 42 deletions(-) create mode 100644 services/common/src/main/java/org/collectionspace/services/common/ConfigurationException.java diff --git a/services/common/src/main/java/org/collectionspace/services/common/ConfigurationException.java b/services/common/src/main/java/org/collectionspace/services/common/ConfigurationException.java new file mode 100644 index 000000000..53477a4bc --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/common/ConfigurationException.java @@ -0,0 +1,65 @@ +package org.collectionspace.services.common; + +public class ConfigurationException extends ServiceException { + /** + * + */ + private static final long serialVersionUID = -6399176494104282779L; + + final public static int HTTP_CODE = 500; + + /** + * Creates a new instance of BadRequestException without detail message. + */ + public ConfigurationException() { + super(HTTP_CODE); + } + + /** + * Constructs an instance of BadRequestException with the specified detail message. + * @param msg the detail message. + */ + public ConfigurationException(String msg) { + super(msg); + setErrorCode(HTTP_CODE); + } + + /** + * 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 ConfigurationException(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 ConfigurationException(Throwable cause) { + super(cause); + setErrorCode(HTTP_CODE); + } + +} diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java index 3a611d7dd..c1465a2c7 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java @@ -341,7 +341,7 @@ public abstract class AbstractServiceContextImpl public String getTenantQualifiedDoctype(String docType) { // If they have not overridden the setting, use the type of the service // object. - String result = docType + ServiceContext.TENANT_SUFFIX + this.getTenantId(); + String result = ServiceBindingUtils.getTenantQualifiedDocType(this.getTenantId(), docType); return result; } diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceBindingUtils.java b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceBindingUtils.java index f3587a53f..051321370 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceBindingUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceBindingUtils.java @@ -24,6 +24,11 @@ public class ServiceBindingUtils { public static final String SERVICE_TYPE_OBJECT = "object"; public static final String SERVICE_TYPE_PROCEDURE = "procedure"; + public static String getTenantQualifiedDocType(String tenantId, String docType) { + String result = docType + ServiceContext.TENANT_SUFFIX + tenantId; + return result; + } + // TODO consider building up a hashTable of the properties for each // service binding. There will be generic properties, as well as // properties on each part. Could build up a key from tenant id, diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java index 3eadf6ed7..118b4ff12 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java @@ -836,7 +836,7 @@ public class RepositoryJavaClientImpl implements RepositoryClient - + + diff --git a/services/imports/service/pom.xml b/services/imports/service/pom.xml index 89a400a50..cab70f3ee 100755 --- a/services/imports/service/pom.xml +++ b/services/imports/service/pom.xml @@ -19,6 +19,12 @@ org.collectionspace.services.common ${project.version} + + org.collectionspace.services + org.collectionspace.services.authentication.service + ${project.version} + provided + org.collectionspace.services org.collectionspace.services.imports.jaxb diff --git a/services/imports/service/src/main/java/org/collectionspace/services/imports/ImportsResource.java b/services/imports/service/src/main/java/org/collectionspace/services/imports/ImportsResource.java index 64af75990..4282bec37 100755 --- a/services/imports/service/src/main/java/org/collectionspace/services/imports/ImportsResource.java +++ b/services/imports/service/src/main/java/org/collectionspace/services/imports/ImportsResource.java @@ -23,6 +23,7 @@ */ package org.collectionspace.services.imports; +import org.collectionspace.services.common.ConfigurationException; import org.collectionspace.services.common.FileUtils; import org.collectionspace.services.common.ResourceBase; import org.collectionspace.services.common.ServiceMain; @@ -31,7 +32,12 @@ import org.collectionspace.services.common.api.FileTools; import org.collectionspace.services.common.api.Tools; import org.collectionspace.services.common.api.ZipTools; import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl; +import org.collectionspace.services.common.tenant.RepositoryDomainType; +import org.collectionspace.services.common.tenant.TenantBindingType; +import org.collectionspace.authentication.AuthN; + import org.collectionspace.services.imports.nuxeo.ImportCommand; +import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; import org.xml.sax.InputSource; @@ -66,6 +72,29 @@ public class ImportsResource extends ResourceBase { public static final String SERVICE_PATH = "imports"; public static final String SERVICE_NAME = "imports"; + /* + * ASSUMPTION: All Nuxeo services of a given tenancy store their stuff in the same repository domain under + * the "workspaces" directory. + * + * Using the tenant ID of the currently authenticated user, this method returns the repository domain name of the + * current tenancy. + */ + private static String getWorkspaces() throws ConfigurationException { + String result = null; + + TenantBindingConfigReaderImpl tReader = ServiceMain.getInstance().getTenantBindingConfigReader(); + TenantBindingType tenantBinding = tReader.getTenantBinding(AuthN.get().getCurrentTenantId()); + List repositoryDomainList = tenantBinding.getRepositoryDomain(); + if (repositoryDomainList.size() == 1) { + String domainName = repositoryDomainList.get(0).getStorageName().trim(); + result = "/" + domainName + "/" + NuxeoUtils.WORKSPACES; + } else { + throw new ConfigurationException("Tenant bindings contains 0 or more than 1 repository domains."); + } + + return result; + } + @Override public String getServiceName(){ return SERVICE_NAME; @@ -145,15 +174,17 @@ public class ImportsResource extends ResourceBase { } public static String createFromInputSource(InputSource inputSource) throws Exception { + String tenantId = AuthN.get().getCurrentTenantId(); // We must expand the request and wrap it with all kinds of Nuxeo baggage, which expandXmlPayloadToDir knows how to do. String outputDir = FileTools.createTmpDir("imports-").getCanonicalPath(); File outpd = new File(outputDir); outpd.mkdirs(); - expandXmlPayloadToDir(inputSource, getTemplateDir(), outpd.getCanonicalPath()); + expandXmlPayloadToDir(tenantId, inputSource, getTemplateDir(), outpd.getCanonicalPath()); // Next, call the nuxeo import service, pointing it to our local directory that has the expanded request. ImportCommand importCommand = new ImportCommand(); - String destWorkspaces = "/default-domain/workspaces"; +// String destWorkspaces = "/default-domain/workspaces"; + String destWorkspaces = getWorkspaces(); String report = "NORESULTS"; try { report = importCommand.run(outputDir, destWorkspaces); @@ -165,15 +196,17 @@ public class ImportsResource extends ResourceBase { } public static String createFromFilename(String filename) throws Exception { + String tenantId = AuthN.get().getCurrentTenantId(); // We must expand the request and wrap it with all kinds of Nuxeo baggage, which expandXmlPayloadToDir knows how to do. String outputDir = FileTools.createTmpDir("imports-").getCanonicalPath(); File outpd = new File(outputDir); outpd.mkdirs(); - expandXmlPayloadToDir(filename, getTemplateDir(), outpd.getCanonicalPath()); + expandXmlPayloadToDir(tenantId, filename, getTemplateDir(), outpd.getCanonicalPath()); // Next, call the nuxeo import service, pointing it to our local directory that has the expanded request. ImportCommand importCommand = new ImportCommand(); - String destWorkspaces = "/default-domain/workspaces"; +// String destWorkspaces = "/default-domain/workspaces"; + String destWorkspaces = getWorkspaces(); String report = importCommand.run(outputDir, destWorkspaces); String result = "SUCCESS"+report+""; return result; @@ -206,13 +239,13 @@ public class ImportsResource extends ResourceBase { return requestFilename; } - public static void expandXmlPayloadToDir(String inputFilename, String templateDir, String outputDir) throws Exception { + public static void expandXmlPayloadToDir(String tenantId, String inputFilename, String templateDir, String outputDir) throws Exception { System.out.println("############## TEMPLATE_DIR: "+templateDir); System.out.println("############## OUTPUT_DIR:"+outputDir); File file = new File(inputFilename); FileInputStream is = new FileInputStream(file); InputSource inputSource = new InputSource(is); - TemplateExpander.expandInputSource(templateDir, outputDir, inputSource, "/imports/import"); + TemplateExpander.expandInputSource(tenantId, templateDir, outputDir, inputSource, "/imports/import"); } /** This method may be called statically from outside this class; there is a test call in @@ -224,10 +257,10 @@ public class ImportsResource extends ResourceBase { * @param templateDir The local directory where templates are to be found at runtime. * @param outputDir The local directory where expanded files and directories are found, ready to be passed to the Nuxeo importer. */ - public static void expandXmlPayloadToDir(InputSource inputSource, String templateDir, String outputDir) throws Exception { + public static void expandXmlPayloadToDir(String tenantId, InputSource inputSource, String templateDir, String outputDir) throws Exception { System.out.println("############## TEMPLATE_DIR: "+templateDir); System.out.println("############## OUTPUT_DIR:"+outputDir); - TemplateExpander.expandInputSource(templateDir, outputDir, inputSource, "/imports/import"); + TemplateExpander.expandInputSource(tenantId, templateDir, outputDir, inputSource, "/imports/import"); //TemplateExpander.expandInputSource(templateDir, outputDir, inputFilename, "/imports/import"); } diff --git a/services/imports/service/src/main/java/org/collectionspace/services/imports/TemplateExpander.java b/services/imports/service/src/main/java/org/collectionspace/services/imports/TemplateExpander.java index d34aa480c..5b4cecb71 100755 --- a/services/imports/service/src/main/java/org/collectionspace/services/imports/TemplateExpander.java +++ b/services/imports/service/src/main/java/org/collectionspace/services/imports/TemplateExpander.java @@ -32,6 +32,9 @@ import org.collectionspace.services.common.XmlSaxFragmenter; import org.collectionspace.services.common.XmlTools; import org.collectionspace.services.common.api.FileTools; import org.collectionspace.services.common.api.Tools; +import org.collectionspace.services.common.datetime.GregorianCalendarDateTimeUtils; +import org.collectionspace.services.nuxeo.util.NuxeoUtils; + import org.dom4j.Document; import org.dom4j.Element; import org.hibernate.sql.Template; @@ -62,7 +65,7 @@ public class TemplateExpander { return Tools.searchAndReplace(source, var(theVar), replace); } - public static String doOneService(String outDir, String partTmpl, String wrapperTmpl, + public static String doOneService(String tenantId, String outDir, String partTmpl, String wrapperTmpl, String SERVICE_TYPE, String SERVICE_NAME, String CSID) throws Exception { String docID; if (Tools.notBlank(CSID)){ @@ -74,9 +77,13 @@ public class TemplateExpander { wrapperTmpl = Tools.searchAndReplace(wrapperTmpl, var("Schema"), part); wrapperTmpl = Tools.searchAndReplace(wrapperTmpl, var("docID"), docID); + wrapperTmpl = Tools.searchAndReplace(wrapperTmpl, var("tenantID"), tenantId); wrapperTmpl = Tools.searchAndReplace(wrapperTmpl, var("ServiceType"), SERVICE_TYPE); wrapperTmpl = Tools.searchAndReplace(wrapperTmpl, var("ServiceName"), SERVICE_NAME); //TODO: set timestamp via creating a ${created} variable. + String nowTime = GregorianCalendarDateTimeUtils.timestampUTC(); + wrapperTmpl = Tools.searchAndReplace(wrapperTmpl, var("createdDate"), nowTime); + wrapperTmpl = Tools.searchAndReplace(wrapperTmpl, var("updatedDate"), nowTime); String serviceDir = outDir+'/'+docID; FileTools.saveFile(serviceDir, "document.xml", wrapperTmpl, true/*true=create parent dirs*/); @@ -96,24 +103,26 @@ public class TemplateExpander { * @param CSID An optional parameter which forces the document CSID, otherwise the CSID is set to a random UUID. * @throws Exception */ - public static void createDocInWorkspace(String partTmpl, - String SERVICE_NAME, - String SERVICE_TYPE, - String TEMPLATE_DIR, - String OUTPUT_DIR, - String CSID) throws Exception { + public static void createDocInWorkspace( + String tenantId, + String partTmpl, + String SERVICE_NAME, + String SERVICE_TYPE, + String TEMPLATE_DIR, + String OUTPUT_DIR, + String CSID) throws Exception { String wrapperTmpl = FileTools.readFile(TEMPLATE_DIR,"service-document.xml"); String outputDir = OUTPUT_DIR+'/'+SERVICE_NAME; - doOneService(outputDir, partTmpl, wrapperTmpl, SERVICE_TYPE, SERVICE_NAME, CSID); + doOneService(tenantId, outputDir, partTmpl, wrapperTmpl, SERVICE_TYPE, SERVICE_NAME, CSID); } - public static void expand(String TEMPLATE_DIR, String outputDir, String requestFilename, String chopPath){ - FragmentHandlerImpl callback = new FragmentHandlerImpl(TEMPLATE_DIR, outputDir); + public static void expand(String tenantId, String TEMPLATE_DIR, String outputDir, String requestFilename, String chopPath){ + FragmentHandlerImpl callback = new FragmentHandlerImpl(tenantId, TEMPLATE_DIR, outputDir); XmlSaxFragmenter.parse(requestFilename, chopPath, callback, false); } - public static void expandInputSource(String TEMPLATE_DIR, String outputDir, InputSource requestSource, String chopPath){ - FragmentHandlerImpl callback = new FragmentHandlerImpl(TEMPLATE_DIR, outputDir); + public static void expandInputSource(String tenantId, String TEMPLATE_DIR, String outputDir, InputSource requestSource, String chopPath){ + FragmentHandlerImpl callback = new FragmentHandlerImpl(tenantId, TEMPLATE_DIR, outputDir); XmlSaxFragmenter.parse(requestSource, chopPath, callback, false); } @@ -124,14 +133,21 @@ public class TemplateExpander { * <import ID="1" service="Personauthorities" type="Personauthority"> */ public static class FragmentHandlerImpl implements IFragmentHandler { + public String SERVICE_NAME = ""; //You can provide a default. + public String SERVICE_TYPE = ""; //You can provide a default. + public String TEMPLATE_DIR = ""; //You MUST provide a default via constructor. + public String OUPUT_DIR = ""; //You MUST provide a default via constructor. + public String TENANT_ID = ""; + //============IFragmentHandler=========================================================== public void onFragmentReady(Document context, Element fragmentParent, String currentPath, int fragmentIndex, String fragment){ try { dump(context, currentPath, fragmentIndex, fragment); String serviceName = checkAttribute(fragmentParent, "service", SERVICE_NAME); String serviceType = checkAttribute(fragmentParent, "type", SERVICE_TYPE); + serviceType = NuxeoUtils.getTenantQualifiedDocType(TENANT_ID, serviceType); //REM - Ensure a tenant qualified Nuxeo doctype String CSID = fragmentParent.attributeValue("CSID"); - TemplateExpander.createDocInWorkspace(fragment, serviceName, serviceType, TEMPLATE_DIR, OUPUT_DIR, CSID); + TemplateExpander.createDocInWorkspace(TENANT_ID, fragment, serviceName, serviceType, TEMPLATE_DIR, OUPUT_DIR, CSID); } catch (Exception e){ System.err.println("ERROR calling expandXmlPayloadToDir"+e); e.printStackTrace(); @@ -141,14 +157,11 @@ public class TemplateExpander { System.out.println("====TemplateExpander DONE============\r\n"+ XmlTools.prettyPrint(document)+"================"); } //============helper methods============================================================== - public FragmentHandlerImpl(String templateDir, String outputDir){ + public FragmentHandlerImpl(String tenantId, String templateDir, String outputDir){ TEMPLATE_DIR = templateDir; OUPUT_DIR = outputDir; + TENANT_ID = tenantId; } - public String SERVICE_NAME = ""; //You can provide a default. - public String SERVICE_TYPE = ""; //You can provide a default. - public String TEMPLATE_DIR = ""; //You MUST provide a default via constructor. - public String OUPUT_DIR = ""; //You MUST provide a default via constructor. private String checkAttribute(Element fragmentParent, String attName, String defaultVal){ String val = fragmentParent.attributeValue(attName); if (Tools.notEmpty(val)){ diff --git a/services/imports/service/src/main/resources/templates/service-document.xml b/services/imports/service/src/main/resources/templates/service-document.xml index e3cef97a1..858fe6a57 100755 --- a/services/imports/service/src/main/resources/templates/service-document.xml +++ b/services/imports/service/src/main/resources/templates/service-document.xml @@ -36,9 +36,9 @@ - 2011-03-05T00:06:17Z - 2011-03-05T00:06:17Z - 1 + ${updatedDate} + ${createdDate} + ${tenantID} ${Schema} diff --git a/services/imports/service/src/test/java/org/collectionspace/services/test/ImportsServiceTest.java b/services/imports/service/src/test/java/org/collectionspace/services/test/ImportsServiceTest.java index 89239a0e9..ec9832010 100755 --- a/services/imports/service/src/test/java/org/collectionspace/services/test/ImportsServiceTest.java +++ b/services/imports/service/src/test/java/org/collectionspace/services/test/ImportsServiceTest.java @@ -45,6 +45,7 @@ public class ImportsServiceTest { //These paths won't work when deployed in jboss, but they will work in the build in the source tree, which is what this test is for. public static final String TEMPLATES_REL_DIR_TO_MODULE = "./src/main/resources/templates"; public static final String REQUESTS_REL_DIR_TO_MODULE = "./src/test/resources/requests"; + public static final String BOGUS_TENANT_ID = "-1"; /** this test just attempts to expand a single file upload to nuxeo's import/export file/directory format, * but does not do the import, so that this test may be run without a nuxeo instance running. @@ -58,7 +59,7 @@ public class ImportsServiceTest { String xmlPayload = FileTools.readFile(REQUESTS_DIR,"authority-request.xml"); InputSource inputSource = ImportsResource.payloadToInputSource(xmlPayload); - ImportsResource.expandXmlPayloadToDir(inputSource, TEMPLATE_DIR, outputDir); + ImportsResource.expandXmlPayloadToDir(BOGUS_TENANT_ID, inputSource, TEMPLATE_DIR, outputDir); //TODO: inspect dir, then *cleanup*!! } diff --git a/services/imports/service/src/test/resources/requests/collectionobject-request.xml b/services/imports/service/src/test/resources/requests/collectionobject-request.xml index 9e4f549b7..49c21c962 100755 --- a/services/imports/service/src/test/resources/requests/collectionobject-request.xml +++ b/services/imports/service/src/test/resources/requests/collectionobject-request.xml @@ -1,13 +1,6 @@ - - - - - - - -- 2.47.3