From 2d3e872132a99d6a55bb648360708b1fabae4017 Mon Sep 17 00:00:00 2001 From: Aron Roberts Date: Thu, 9 Aug 2012 20:42:12 -0700 Subject: [PATCH] CSPACE-5271: Reworked UriTemplateRegistry to make the registry's entries tenant-qualified. Need to fix a bug in CollectionSpaceJaxRsApplication that causes each new set of entries to stomp on the existing map, and rework the test class to reflect the tenant-qualification change. --- .../CollectionSpaceJaxRsApplication.java | 34 +++++- .../common/vocabulary/AuthorityResource.java | 23 ++-- .../services/common/ResourceBase.java | 102 ++++++++++++------ .../common/StoredValuesUriTemplate.java | 18 ++-- .../services/common/UriTemplateRegistry.java | 19 ++-- .../common/UriTemplateRegistryHolder.java | 6 ++ .../AuthorityResourceWithContacts.java | 21 ++-- 7 files changed, 157 insertions(+), 66 deletions(-) create mode 100644 services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistryHolder.java diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java index c34b83663..6910ea3f4 100644 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java @@ -82,12 +82,12 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory; * $LastChangedDate$ */ public class CollectionSpaceJaxRsApplication extends Application - implements ResourceMapHolder { + implements ResourceMapHolder, UriTemplateRegistryHolder { private Set singletons = new HashSet(); private Set> empty = new HashSet>(); private ResourceMap resourceMap = new ResourceMapImpl(); - private UriTemplateRegistry uriTemplateRegistry = new UriTemplateRegistry(); + private static UriTemplateRegistry uriTemplateRegistry = new UriTemplateRegistry(); private ServletContext servletContext = null; public CollectionSpaceJaxRsApplication() { @@ -129,6 +129,7 @@ public class CollectionSpaceJaxRsApplication extends Application singletons.add(new IDResource()); + buildUriTemplateRegistry(); // FIXME: Temporary for CSPACE-5271 - please remove once // that issue is resolved uriTemplateRegistry.dump(); @@ -144,16 +145,35 @@ public class CollectionSpaceJaxRsApplication extends Application private void addResourceToMapAndSingletons(ResourceBase resource) { singletons.add(resource); resourceMap.put(resource.getServiceName(), resource); + } + + /** + * Build a registry of URI templates by querying each resource + * for its own entry in the registry. + * + * That entry consists of a tenant-qualified map of URI templates, each + * associated with a specific document type + */ + private void buildUriTemplateRegistry() { + ResourceBase resource = null; + ResourceMap resources = getResourceMap(); + for (Map.Entry entry : resources.entrySet()) { + resource = entry.getValue(); + System.out.println(resource.getServiceName()); // for debugging + getUriTemplateRegistry().putAll(resource.getUriRegistryEntries()); + getUriTemplateRegistry().dump(); // for debugging + } // Contacts itself should not have an entry in the URI template registry; // there should be a Contacts entry in that registry only for use in // building URIs for resources that have contacts as a sub-resource // // FIXME: There may be a more elegant way to filter this out; or it may // fall out during implementation of CSPACE-2698 - if (! (resource instanceof ContactResource)) { - uriTemplateRegistry.putAll(resource.getUriTemplateMap()); - } + // + // final String CONTACT_DOCTYPE = "Contact"; + // uriTemplateRegistry.remove(CONTACT_DOCTYPE); } + @Override public Set> getClasses() { @@ -169,6 +189,10 @@ public class CollectionSpaceJaxRsApplication extends Application return resourceMap; } + public UriTemplateRegistry getUriTemplateRegistry() { + return uriTemplateRegistry; + } + public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } diff --git a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java index 852546b01..37aa1ae66 100644 --- a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java +++ b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java @@ -961,20 +961,27 @@ public abstract class AuthorityResource } } - public String getItemDocType() { - return super.getDocType(getItemServiceName()); + protected String getItemDocType(String tenantId) { + return super.getDocType(tenantId, getItemServiceName()); } + /** + * Constructs and returns a map of URI templates for the current resource, + * for the specified tenant + * + * @param tenantId a tenant ID + * @return a map of URI templates for the current resource, for the specified tenant + */ @Override - public Map getUriTemplateMap() { - // Get resource URI template from superclass - Map uriTemplateMap = super.getUriTemplateMap(); - // Add item URI template, and return both templates in the map - String itemDocType = getItemDocType(); - StoredValuesUriTemplate itemUriTemplate = getUriTemplate(UriTemplateFactory.ITEM); + protected Map getUriTemplateMap(String tenantId) { + // Get the resource URI template from the superclass + Map uriTemplateMap = super.getUriTemplateMap(tenantId); + // Add the item URI template here, and return both templates in the map + String itemDocType = getItemDocType(tenantId); if (itemDocType == null) { return uriTemplateMap; // return map as obtained from superclass } + StoredValuesUriTemplate itemUriTemplate = getUriTemplate(UriTemplateFactory.ITEM); if (itemUriTemplate == null) { return uriTemplateMap; // return map as obtained from superclass } diff --git a/services/common/src/main/java/org/collectionspace/services/common/ResourceBase.java b/services/common/src/main/java/org/collectionspace/services/common/ResourceBase.java index 6d2fec6b0..0a3ba97f4 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/ResourceBase.java +++ b/services/common/src/main/java/org/collectionspace/services/common/ResourceBase.java @@ -413,41 +413,54 @@ public abstract class ResourceBase return getDocModelForAuthorityItem(repoSession, RefName.AuthorityItem.parse(refName)); } - public String getDocType() { - return getDocType(getServiceName()); + protected String getDocType(String tenantId) { + return getDocType(tenantId, getServiceName()); } - - // FIXME: The technique in getDocType(String) may well be a dreadful hack, just to get this initially working. - // - // Question: - // At the point we're seeking to populate docTypes in the uriTemplateRegistry, during system startup, - // we're not yet logged into any tenant, but instead it appears we are acting as the user SPRING_ADMIN. - // Could this potentially suggest reasonable method(s), other than those below, which reads from - // tenant bindings configuration for an arbitrary tenant, by which we can obtain the docType(s) - // associated with the current resource? - public String getDocType(String serviceName) { + // FIXME: This method may properly belong in a different services package or class. + protected String getDocType(String tenantId, String serviceName) { String docType = ""; - String anyTenantId = ""; - TenantBindingConfigReaderImpl reader = ServiceMain.getInstance().getTenantBindingConfigReader(); - // FIXME: Makes the likely unsupportable assumption that the list of service names and associated - // document types is materially identical across tenants - anyTenantId = getAnyTenantId(reader); - if (Tools.notBlank(anyTenantId)) { - ServiceBindingType sb = reader.getServiceBinding(anyTenantId, serviceName); - docType = sb.getObject().getName(); // reads the Nuxeo Document Type from tenant bindings configuration + if (Tools.notBlank(tenantId)) { + ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, serviceName); + docType = sb.getObject().getName(); // Reads the Nuxeo Document Type from tenant bindings configuration } return docType; } - public Map getUriTemplateMap() { - Map uriTemplateMap = new HashMap(); - // Construct and return a resource URI template as the sole item in the map - String docType = getDocType(); - StoredValuesUriTemplate resourceUriTemplate = getUriTemplate(UriTemplateFactory.RESOURCE); + /** + * Returns a UriRegistry entry: a map of tenant-qualified URI templates + * for the current resource, for all tenants + * + * @return a map of URI templates for the current resource, for all tenants + */ + public HashMap> getUriRegistryEntries() { + HashMap> uriRegistryEntriesMap = + new HashMap>(); + List tenantIds = getTenantIds(); + for (String tenantId : tenantIds) { + uriRegistryEntriesMap.put(tenantId, getUriTemplateMap(tenantId)); + } + return uriRegistryEntriesMap; + } + + + /** + * Constructs and returns a map of URI templates for the current resource, + * for the specified tenant + * + * @param tenantId a tenant ID + * @return a map of URI templates for the current resource, for the specified tenant + */ + protected Map getUriTemplateMap(String tenantId) { + Map uriTemplateMap = new HashMap(); + if (tenantId == null) { + return uriTemplateMap; // return an empty map + } + String docType = getDocType(tenantId); if (docType == null) { return uriTemplateMap; // return an empty map } + StoredValuesUriTemplate resourceUriTemplate = getUriTemplate(UriTemplateFactory.RESOURCE); if (resourceUriTemplate == null) { return uriTemplateMap; // return an empty map } @@ -455,6 +468,13 @@ public abstract class ResourceBase return uriTemplateMap; } + /** + * Returns a UriTemplate of the appropriate type, populated with the + * current service name as one of its stored values. + * + * @param type a UriTemplate type + * @return a UriTemplate of the appropriate type + */ protected StoredValuesUriTemplate getUriTemplate(UriTemplateFactory.UriTemplateType type) { Map storedValuesMap = new HashMap(); storedValuesMap.put(UriTemplateFactory.SERVICENAME_VAR, getServiceName()); @@ -463,19 +483,37 @@ public abstract class ResourceBase return template; } - public String getAnyTenantId(TenantBindingConfigReaderImpl reader) { - String anyTenantId = ""; - Hashtable tenantBindings = reader.getTenantBindings(); + /** + * Returns a list of tenant IDs, from tenant bindings configuration + * + * @return a list of tenant IDs + */ + // FIXME: This method may properly belong in a different services package or class. + protected List getTenantIds() { + List tenantIds = new ArrayList(); + String tenantId; + Hashtable tenantBindings = + getTenantBindingsReader().getTenantBindings(); if (tenantBindings != null && !tenantBindings.isEmpty()) { Enumeration keys = tenantBindings.keys(); while (keys.hasMoreElements()) { - anyTenantId = (String) keys.nextElement(); - if (Tools.notBlank(anyTenantId)) { - break; + tenantId = (String) keys.nextElement(); + if (Tools.notBlank(tenantId)) { + tenantIds.add(tenantId); } } } - return anyTenantId; + return tenantIds; } + + /** + * Returns a reader for reading values from tenant bindings configuration + * + * @return a tenant bindings configuration reader + */ + protected TenantBindingConfigReaderImpl getTenantBindingsReader() { + return ServiceMain.getInstance().getTenantBindingConfigReader(); + } + } diff --git a/services/common/src/main/java/org/collectionspace/services/common/StoredValuesUriTemplate.java b/services/common/src/main/java/org/collectionspace/services/common/StoredValuesUriTemplate.java index 60fb2340f..315f2898c 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/StoredValuesUriTemplate.java +++ b/services/common/src/main/java/org/collectionspace/services/common/StoredValuesUriTemplate.java @@ -35,10 +35,11 @@ import org.slf4j.LoggerFactory; * replace variables within the template. * * In this subclass of UriTemplate, some of the values which will replace - * variables in an internal URI template are static, and can be stored alongside - * the URI template for reuse. Additional values that will replace variables - * within the template are also dynamically accepted, and will be merged with - * stored values when building URIs. + * variables in an internal URI template are typically stable / static, and + * can be stored alongside the URI template for reuse each time that a URI + * is built from the template. Additional values that will replace variables + * within the template on a one-off basis are also dynamically accepted, + * and will be merged with the previously-stored values when building URIs. */ public class StoredValuesUriTemplate extends UriTemplate { @@ -72,10 +73,11 @@ public class StoredValuesUriTemplate extends UriTemplate { } /** - * Builds a URI string from a combination of previously-stored values, if - * any (such as static URI path components) and additional values, if any - * (such as resource identifiers), both of which will replace variables - * within the URI template. + * Builds a URI string from a combination of values previously stored + * along with the template, if any (typically stable URI path components, + * such as "intakes" and "personauthorities") and additional values, if any + * (typically CSIDs and URN-based resource identifiers), both of which will + * replace variables within the URI template. * * @param additionalValuesMap an optional map of values that will replace * variables within the URI template diff --git a/services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistry.java b/services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistry.java index 18131a825..3929120e0 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistry.java +++ b/services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistry.java @@ -23,19 +23,26 @@ package org.collectionspace.services.common; import java.util.HashMap; +import java.util.Map; /** * UriTemplateRegistry.java * - * Maps document types to templates for building URIs, used in turn for - * accessing instances of those documents. + * Maps document types to templates for building URIs, per tenant. */ -public class UriTemplateRegistry extends HashMap { +public class UriTemplateRegistry extends HashMap> { + // For debugging public void dump() { - for (String docTypeKey : this.keySet()) { - StoredValuesUriTemplate uriTemplate = this.get(docTypeKey); - System.out.println("Key = " + docTypeKey + ", Value = " + uriTemplate.getUriTemplateType() + " : " + uriTemplate.toString()); + for (String tenantId : this.keySet()) { + System.out.println("###############################"); + System.out.println("Tenant ID = " + tenantId); + System.out.println("###############################"); + for (Map.Entry uriTemplateEntry : this.get(tenantId).entrySet()) { + System.out.println("Key = " + uriTemplateEntry.getKey() + + ", Value = " + uriTemplateEntry.getValue().getUriTemplateType() + + " : " + uriTemplateEntry.getValue().toString()); + } } } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistryHolder.java b/services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistryHolder.java new file mode 100644 index 000000000..a3ab98b03 --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/common/UriTemplateRegistryHolder.java @@ -0,0 +1,6 @@ + +package org.collectionspace.services.common; + +public interface UriTemplateRegistryHolder { + public UriTemplateRegistry getUriTemplateRegistry(); +} diff --git a/services/contact/service/src/main/java/org/collectionspace/services/contact/AuthorityResourceWithContacts.java b/services/contact/service/src/main/java/org/collectionspace/services/contact/AuthorityResourceWithContacts.java index 9d6f219ea..fc581dd27 100644 --- a/services/contact/service/src/main/java/org/collectionspace/services/contact/AuthorityResourceWithContacts.java +++ b/services/contact/service/src/main/java/org/collectionspace/services/contact/AuthorityResourceWithContacts.java @@ -297,24 +297,31 @@ public abstract class AuthorityResourceWithContacts } } - public String getContactDocType() { + protected String getContactDocType() { return ContactConstants.NUXEO_DOCTYPE; } - // This currently populates only one entry in the UriTemplateRegistry + /** + * Constructs and returns a map of URI templates for the current resource, + * for the specified tenant + * + * @param tenantId a tenant ID + * @return a map of URI templates for the current resource, for the specified tenant + */ + // FIXME: This method currently populates only one entry in the UriTemplateRegistry // for the Contacts docType, even though contacts can be a sub-resource of // multiple authority item resources. (This method may be called more than once, // but each time the existing item with the same key in the map is overwritten.) @Override - public Map getUriTemplateMap() { - // Get resource and item URI templates from superclass - Map uriTemplateMap = super.getUriTemplateMap(); - // Add contact URI template, and return all three templates in the map + protected Map getUriTemplateMap(String tenantId) { + // Get the resource and item URI templates from the superclass + Map uriTemplateMap = super.getUriTemplateMap(tenantId); + // Add the contact URI template here, and return all three templates in the map String contactDocType = getContactDocType(); - StoredValuesUriTemplate contactUriTemplate = getUriTemplate(UriTemplateFactory.CONTACT); if (contactDocType == null) { return uriTemplateMap; // return map as obtained from superclass } + StoredValuesUriTemplate contactUriTemplate = getUriTemplate(UriTemplateFactory.CONTACT); if (contactUriTemplate == null) { return uriTemplateMap; // return map as obtained from superclass } -- 2.47.3