2 * This document is a part of the source code and related artifacts
3 * for CollectionSpace, an open source collections management system
4 * for museums and related institutions:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 package org.collectionspace.services.common.config;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.util.ArrayList;
31 import java.util.Hashtable;
32 import java.util.List;
34 import org.apache.commons.io.FileUtils;
35 import org.collectionspace.services.common.service.ServiceBindingType;
36 import org.collectionspace.services.common.service.ServiceObjectType;
37 import org.collectionspace.services.common.tenant.RepositoryDomainType;
38 import org.collectionspace.services.common.tenant.TenantBindingType;
39 import org.collectionspace.services.common.tenant.TenantBindingConfig;
40 import org.collectionspace.services.common.types.PropertyItemType;
42 import ch.elca.el4j.services.xmlmerge.Configurer;
43 import ch.elca.el4j.services.xmlmerge.config.AttributeMergeConfigurer;
44 import ch.elca.el4j.services.xmlmerge.config.ConfigurableXmlMerge;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * ServicesConfigReader reads service layer specific configuration
52 * $LastChangedRevision: $
55 public class TenantBindingConfigReaderImpl
56 extends AbstractConfigReaderImpl<List<TenantBindingType>> {
57 final private static String TENANT_BINDINGS_ERROR = "Tenant bindings error: ";
58 final private static String TENANT_BINDINGS_ROOTDIRNAME = "tenants";
59 final private static String TENANT_BINDINGS_FILENAME_PREFIX = "tenant-bindings";
60 final private static String TENANT_BINDINGS_PROTOTYPE_FILENAME = TENANT_BINDINGS_FILENAME_PREFIX + "-proto.xml";
61 final private static String TENANT_BINDINGS_DELTA_FILENAME = TENANT_BINDINGS_FILENAME_PREFIX + ".delta.xml";
62 final private static String MERGED_SUFFIX = ".merged.xml";
64 final Logger logger = LoggerFactory.getLogger(TenantBindingConfigReaderImpl.class);
65 private List<TenantBindingType> tenantBindingTypeList;
66 //tenant id, tenant binding
67 private Hashtable<String, TenantBindingType> tenantBindings =
68 new Hashtable<String, TenantBindingType>();
70 private Hashtable<String, RepositoryDomainType> domains =
71 new Hashtable<String, RepositoryDomainType>();
72 //tenant-qualified servicename, service binding
73 private Hashtable<String, ServiceBindingType> serviceBindings =
74 new Hashtable<String, ServiceBindingType>();
76 //tenant-qualified service object name to service name, service binding
77 private Hashtable<String, ServiceBindingType> docTypes =
78 new Hashtable<String, ServiceBindingType>();
81 public TenantBindingConfigReaderImpl(String serverRootDir) {
86 public String getFileName() {
87 return TENANT_BINDINGS_DELTA_FILENAME;
90 protected File getTenantsRootDir() {
92 String errMessage = null;
94 String tenantsRootPath = getConfigRootDir() + File.separator + TENANT_BINDINGS_ROOTDIRNAME;
95 File tenantsRootDir = new File(tenantsRootPath);
96 if (tenantsRootDir.exists() == true) {
97 result = tenantsRootDir;
98 if (logger.isDebugEnabled() == true) {
99 logger.debug("The home directory for all tenants is at: " + result.getCanonicalPath());
102 errMessage = "The home directory for all tenants is missing or inaccesible: ";
104 errMessage = errMessage + tenantsRootDir.getCanonicalPath();
105 } catch (IOException ioException) {
106 errMessage = errMessage + tenantsRootDir.getAbsolutePath();
109 } catch (IOException e) {
110 // Log this exception, but continue anyway. Caller should handle the null result gracefully.
114 if (errMessage != null) {
115 logger.error(errMessage);
122 * Take the directory of the prototype bindings and the directory of the delta bindings. Merge the two and create (replace) a file
123 * named "tenant-bindings.xml"
126 private InputStream merge(File srcFile, File deltaFile) throws IOException {
127 InputStream result = null;
129 FileInputStream srcStream = new FileInputStream(srcFile);
130 FileInputStream deltaStream = new FileInputStream(deltaFile);
131 InputStream[] inputStreamArray = {srcStream, deltaStream};
133 Configurer configurer = new AttributeMergeConfigurer();
134 result = new ConfigurableXmlMerge(configurer).merge(inputStreamArray);
135 } catch (Exception e) {
136 logger.error("Could not merge tenant configuration delta file: " +
137 deltaFile.getCanonicalPath(), e);
140 // Try to save the merge output to a file that is suffixed with ".merged.xml" in the same directory
141 // as the delta file.
143 if (result != null) {
144 File outputDir = deltaFile.getParentFile();
145 String mergedFileName = outputDir.getAbsolutePath() + File.separator +
146 TenantBindingConfigReaderImpl.TENANT_BINDINGS_FILENAME_PREFIX + MERGED_SUFFIX;
147 File mergedOutFile = new File(mergedFileName);
149 FileUtils.copyInputStreamToFile(result, mergedOutFile);
150 } catch (IOException e) {
151 logger.warn("Could not create a copy of the merged tenant configuration at: " +
154 result.reset(); //reset the stream even if the file create failed.
161 public void read() throws Exception {
162 String tenantsRootPath = getTenantsRootDir().getAbsolutePath();
163 read(tenantsRootPath);
167 public void read(String tenantRootDirPath) throws Exception {
168 File tenantsRootDir = new File(tenantRootDirPath);
169 if (tenantsRootDir.exists() == false) {
170 throw new Exception("Cound not find tenant bindings root directory: " +
173 File protoBindingsFile = new File(tenantRootDirPath + File.separator +
174 TENANT_BINDINGS_PROTOTYPE_FILENAME);
176 List<File> tenantDirs = getDirectories(tenantsRootDir);
177 tenantBindingTypeList = readTenantConfigs(protoBindingsFile, tenantDirs);
179 for (TenantBindingType tenantBinding : tenantBindingTypeList) {
180 tenantBindings.put(tenantBinding.getId(), tenantBinding);
181 readDomains(tenantBinding);
182 readServiceBindings(tenantBinding);
183 if (logger.isInfoEnabled()) {
184 logger.info("Finished reading tenant bindings for tenant id=" + tenantBinding.getId()
185 + " name=" + tenantBinding.getName());
191 * Take the directory of the prototype bindings and the directory of the delta bindings. Merge the two and create (replace) a file
192 * named "tenant-bindings.xml"
194 private static String merge(String original, String patch) {
195 InputStream result = null;
197 Configurer configurer = new AttributeMergeConfigurer();
200 FileInputStream ins1 = new FileInputStream(".\\src\\main\\resources\\File1.xml");
201 FileInputStream ins2 = new FileInputStream(".\\src\\main\\resources\\File2.xml");
202 InputStream[] inputStreamArray = {ins1, ins2};
204 result = new ConfigurableXmlMerge(configurer).merge(inputStreamArray);
205 // result = new ConfigurableXmlMerge(configurer).merge(new String[] {original, patch});
206 } catch (Exception e) {
207 // TODO Auto-generated catch block
210 File mergedOutFile = new File(".\\target\\merged.xml");
212 FileUtils.copyInputStreamToFile(result, mergedOutFile);
213 } catch (IOException e) {
214 // TODO Auto-generated catch block
223 * Merge and read the prototype bindsings with each tenant specific bindings delta to create the final
226 * @param protoBindingsFile - The prototypical bindings file.
227 * @param tenantDirList - The list of tenant directories containing tenant specific bindings
228 * @return A list of tenant bindings.
229 * @throws IOException Signals that an I/O exception has occurred.
231 List<TenantBindingType> readTenantConfigs(File protoBindingsFile, List<File> tenantDirList) throws IOException {
232 List<TenantBindingType> result = new ArrayList<TenantBindingType>();
234 // Iterate through a list of directories.
236 for (File tenantDir : tenantDirList) {
237 boolean found = false;
238 String errMessage = null;
239 File configFile = new File(tenantDir.getAbsoluteFile() + File.separator + getFileName());
240 if (configFile.exists() == true) {
241 InputStream tenantBindingsStream = this.merge(protoBindingsFile, configFile);
242 TenantBindingConfig tenantBindingConfig = null;
244 tenantBindingConfig = (TenantBindingConfig) parse(tenantBindingsStream,
245 TenantBindingConfig.class);
246 } catch (Exception e) {
247 logger.error("Could not parse the merged tenant bindings.", e);
249 if (tenantBindingConfig != null) {
250 TenantBindingType binding = tenantBindingConfig.getTenantBinding();
251 if (binding != null) {
254 if (logger.isInfoEnabled() == true) {
255 logger.info("Parsed tenant configureation for: " + binding.getDisplayName());
258 errMessage = "Cound not parse the tentant bindings in: ";
261 errMessage = "Could not parse the tenant bindings file: ";
264 errMessage = "Cound not find a tenant configuration file: ";
266 if (found == false) {
267 if (logger.isErrorEnabled() == true) {
268 errMessage = errMessage != null ? errMessage : TENANT_BINDINGS_ERROR;
269 logger.error(errMessage + configFile.getAbsolutePath());
277 private void readDomains(TenantBindingType tenantBinding) throws Exception {
278 for (RepositoryDomainType domain : tenantBinding.getRepositoryDomain()) {
279 String key = getTenantQualifiedIdentifier(tenantBinding.getId(),
281 domains.put(key, domain);
285 private void readServiceBindings(TenantBindingType tenantBinding) throws Exception {
286 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
287 String key = getTenantQualifiedServiceName(tenantBinding.getId(),
288 serviceBinding.getName());
289 serviceBindings.put(key, serviceBinding);
291 if (serviceBinding!=null){
292 ServiceObjectType objectType = serviceBinding.getObject();
293 if (objectType!=null){
294 String docType = objectType.getName();
295 String docTypeKey = getTenantQualifiedIdentifier(tenantBinding.getId(), docType);
296 docTypes.put(docTypeKey, serviceBinding);
299 if (logger.isTraceEnabled()) {
300 logger.trace("readServiceBindings() added service "
302 + " workspace=" + serviceBinding.getName());
308 public List<TenantBindingType> getConfiguration() {
309 return tenantBindingTypeList;
313 * getTenantBindings returns all the tenant bindings read from configuration
316 public Hashtable<String, TenantBindingType> getTenantBindings() {
317 return tenantBindings;
321 * getTenantBinding gets tenant binding for given tenant
325 public TenantBindingType getTenantBinding(
327 return tenantBindings.get(tenantId);
331 * getRepositoryDomain gets repository domain configuration for the given name
335 public RepositoryDomainType getRepositoryDomain(String domainName) {
336 return domains.get(domainName.trim());
340 * getRepositoryDomain gets repository domain configuration for the given service
341 * and given tenant id
346 public RepositoryDomainType getRepositoryDomain(String tenantId, String serviceName) {
347 ServiceBindingType serviceBinding = getServiceBinding(tenantId, serviceName);
348 if (serviceBinding == null) {
349 throw new IllegalArgumentException("no service binding found for " + serviceName
350 + " of tenant with id=" + tenantId);
352 String repoDomain = serviceBinding.getRepositoryDomain();
353 if (repoDomain == null) {
354 /* This is excessive - every call to a JPA based service dumps this msg.
355 if (logger.isDebugEnabled()) {
356 logger.debug("No repository domain configured for " + serviceName
357 + " of tenant with id=" + tenantId);
362 String key = this.getTenantQualifiedIdentifier(tenantId, repoDomain.trim());
363 return domains.get(key);
367 * getServiceBinding gets service binding for given tenant for a given service
372 public ServiceBindingType getServiceBinding(
373 String tenantId, String serviceName) {
374 String key = getTenantQualifiedServiceName(tenantId, serviceName);
375 return serviceBindings.get(key);
379 * getServiceBinding gets service binding for given tenant for a given service
384 public ServiceBindingType getServiceBindingForDocType (String tenantId, String docType) {
385 String key = getTenantQualifiedIdentifier(tenantId, docType);
386 return docTypes.get(key);
390 * getServiceBinding gets service binding for given tenant for a given service
395 public List<ServiceBindingType> getServiceBindingsByType(
396 String tenantId, String serviceType) {
397 List<String> serviceTypes = new ArrayList<String>(1);
398 serviceTypes.add(serviceType);
399 return getServiceBindingsByType(tenantId, serviceTypes);
402 * getServiceBinding gets service binding for given tenant for a given service
407 public List<ServiceBindingType> getServiceBindingsByType(
408 String tenantId, List<String> serviceTypes) {
409 ArrayList<ServiceBindingType> list = null;
410 TenantBindingType tenant = tenantBindings.get(tenantId);
411 if (tenant != null) {
412 for (ServiceBindingType sb : tenant.getServiceBindings()) {
413 if (serviceTypes.contains(sb.getType())) {
415 list = new ArrayList<ServiceBindingType>();
427 * @return the properly qualified service name
429 public static String getTenantQualifiedServiceName(
430 String tenantId, String serviceName) {
431 // return tenantId + "." + serviceName.toLowerCase();
432 return getTenantQualifiedIdentifier(tenantId, serviceName.toLowerCase());
435 public static String getTenantQualifiedIdentifier(String tenantId, String identifier) {
436 return tenantId + "." + identifier;
439 * Sets properties in the passed list on the local properties for this TenantBinding.
440 * Note: will only set properties not already set on the TenantBinding.
443 * @param propagateToServices If true, recurses to set set properties
444 * on the associated services.
446 public void setDefaultPropertiesOnTenants(List<PropertyItemType> propList,
447 boolean propagateToServices) {
448 // For each tenant, set properties in list that are not already set
449 if (propList == null || propList.isEmpty()) {
452 for (TenantBindingType tenant : tenantBindings.values()) {
453 for (PropertyItemType prop : propList) {
454 TenantBindingUtils.setPropertyValue(tenant,
455 prop, TenantBindingUtils.SET_PROP_IF_MISSING);
457 if (propagateToServices) {
458 TenantBindingUtils.propagatePropertiesToServices(tenant,
459 TenantBindingUtils.SET_PROP_IF_MISSING);
464 public String getResourcesDir(){
465 return getConfigRootDir() + File.separator + "resources";