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.Enumeration;
32 import java.util.Hashtable;
33 import java.util.List;
35 import org.apache.commons.io.FileUtils;
36 import org.collectionspace.services.common.ServiceMain;
37 import org.collectionspace.services.common.api.JEEServerDeployment;
38 import org.collectionspace.services.common.api.Tools;
39 import org.collectionspace.services.config.service.ServiceBindingType;
40 import org.collectionspace.services.config.service.ServiceObjectType;
41 import org.collectionspace.services.config.tenant.RepositoryDomainType;
42 import org.collectionspace.services.config.tenant.TenantBindingConfig;
43 import org.collectionspace.services.config.tenant.TenantBindingType;
44 import org.collectionspace.services.config.types.PropertyItemType;
46 import ch.elca.el4j.services.xmlmerge.Configurer;
47 import ch.elca.el4j.services.xmlmerge.config.AttributeMergeConfigurer;
48 import ch.elca.el4j.services.xmlmerge.config.ConfigurableXmlMerge;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * ServicesConfigReader reads service layer specific configuration
56 * $LastChangedRevision: $
59 public class TenantBindingConfigReaderImpl
60 extends AbstractConfigReaderImpl<List<TenantBindingType>> {
61 final private static String TENANT_BINDINGS_ERROR = "Tenant bindings error(s) for tenant: ";
62 final private static String TENANT_BINDINGS_DELTA_FILENAME = JEEServerDeployment.TENANT_BINDINGS_FILENAME_PREFIX + ".delta.xml";
63 final private static String MERGED_SUFFIX = ".merged.xml";
64 private static final String NO_SERVICE_BINDINGS_FOUND_ERR = "No Service bindings found.";
66 final Logger logger = LoggerFactory.getLogger(TenantBindingConfigReaderImpl.class);
67 private List<TenantBindingType> tenantBindingTypeList;
68 //tenant id, tenant binding
69 private Hashtable<String, TenantBindingType> tenantBindings =
70 new Hashtable<String, TenantBindingType>();
72 private Hashtable<String, RepositoryDomainType> domains =
73 new Hashtable<String, RepositoryDomainType>();
74 //tenant-qualified servicename, service binding
75 private Hashtable<String, ServiceBindingType> serviceBindings =
76 new Hashtable<String, ServiceBindingType>();
78 //tenant-qualified service object name to service name, service binding
79 private Hashtable<String, ServiceBindingType> docTypes =
80 new Hashtable<String, ServiceBindingType>();
83 public TenantBindingConfigReaderImpl(String serverRootDir) {
88 public String getFileName() {
89 return TENANT_BINDINGS_DELTA_FILENAME;
92 private String getFileName(String tenantName, boolean useAppGeneratedBindings) {
93 String result = getFileName();
95 if (useAppGeneratedBindings == true) {
96 result = tenantName + "-" + result;
103 protected File getTenantsRootDir() {
105 String errMessage = null;
107 String tenantsRootPath = getConfigRootDir() + File.separator + JEEServerDeployment.TENANT_BINDINGS_ROOTDIRNAME;
108 File tenantsRootDir = new File(tenantsRootPath);
109 if (tenantsRootDir.exists() == true) {
110 result = tenantsRootDir;
111 if (logger.isDebugEnabled() == true) {
112 logger.debug("The home directory for all tenants is at: " + result.getCanonicalPath());
115 errMessage = "The home directory for all tenants is missing or inaccesible: ";
117 errMessage = errMessage + tenantsRootDir.getCanonicalPath();
118 } catch (IOException ioException) {
119 errMessage = errMessage + tenantsRootDir.getAbsolutePath();
122 } catch (IOException e) {
123 // Log this exception, but continue anyway. Caller should handle the null result gracefully.
127 if (errMessage != null) {
128 logger.error(errMessage);
135 * Take the directory of the prototype bindings and the directory of the delta bindings. Merge the two and create (replace) a file
136 * named "tenant-bindings.xml"
139 private InputStream merge(File srcFile, File deltaFile) throws IOException {
140 InputStream result = null;
142 FileInputStream srcStream = new FileInputStream(srcFile);
143 FileInputStream deltaStream = new FileInputStream(deltaFile);
144 InputStream[] inputStreamArray = {srcStream, deltaStream};
146 Configurer configurer = new AttributeMergeConfigurer();
147 result = new ConfigurableXmlMerge(configurer).merge(inputStreamArray);
148 } catch (Exception e) {
149 logger.error("Could not merge tenant configuration delta file: " +
150 deltaFile.getCanonicalPath(), e);
153 // Try to save the merge output to a file that is suffixed with ".merged.xml" in the same directory
154 // as the delta file.
156 if (result != null) {
157 File outputDir = deltaFile.getParentFile();
158 String mergedFileName = outputDir.getAbsolutePath() + File.separator +
159 JEEServerDeployment.TENANT_BINDINGS_FILENAME_PREFIX + MERGED_SUFFIX;
160 File mergedOutFile = new File(mergedFileName);
162 FileUtils.copyInputStreamToFile(result, mergedOutFile);
163 } catch (IOException e) {
164 logger.warn("Could not create a copy of the merged tenant configuration at: " +
167 result.reset(); //reset the stream even if the file create failed.
174 public void read(boolean useAppGeneratedBindings) throws Exception {
175 String tenantsRootPath = getTenantsRootDir().getAbsolutePath();
176 read(tenantsRootPath, useAppGeneratedBindings);
180 public void read(String tenantRootDirPath, boolean useAppGeneratedBindings) throws Exception {
181 File tenantsRootDir = new File(tenantRootDirPath);
182 if (tenantsRootDir.exists() == false) {
183 throw new Exception("Cound not find tenant bindings root directory: " +
187 List<File> tenantDirs = getDirectories(tenantsRootDir);
188 tenantBindingTypeList = readTenantConfigs(new File(tenantRootDirPath), tenantDirs, useAppGeneratedBindings);
189 if (tenantBindingTypeList == null || tenantBindingTypeList.size() < 1) {
190 throw new Exception(NO_SERVICE_BINDINGS_FOUND_ERR);
193 for (TenantBindingType tenantBinding : tenantBindingTypeList) {
194 if (tenantBindings.get(tenantBinding.getId()) != null) {
195 TenantBindingType tenantBindingOld = tenantBindings.get(tenantBinding.getId());
196 logger.error("Ignoring duplicate binding definition for tenant id="
197 + tenantBinding.getId()
198 + " existing name=" + tenantBindingOld.getName()
199 + " conflicting (ignored) name=" + tenantBinding.getName());
202 tenantBindings.put(tenantBinding.getId(), tenantBinding);
203 readDomains(tenantBinding);
204 readServiceBindings(tenantBinding);
205 if (logger.isInfoEnabled()) {
206 logger.info("Finished reading tenant bindings for tenant id=" + tenantBinding.getId()
207 + " name=" + tenantBinding.getName());
213 * Take the directory of the prototype bindings and the directory of the delta bindings. Merge the two and create (replace) a file
214 * named "tenant-bindings.xml"
216 private static String merge(String original, String patch) {
217 InputStream result = null;
219 Configurer configurer = new AttributeMergeConfigurer();
222 FileInputStream ins1 = new FileInputStream(".\\src\\main\\resources\\File1.xml");
223 FileInputStream ins2 = new FileInputStream(".\\src\\main\\resources\\File2.xml");
224 InputStream[] inputStreamArray = {ins1, ins2};
226 result = new ConfigurableXmlMerge(configurer).merge(inputStreamArray);
227 // result = new ConfigurableXmlMerge(configurer).merge(new String[] {original, patch});
228 } catch (Exception e) {
229 // TODO Auto-generated catch block
232 File mergedOutFile = new File(".\\target\\merged.xml");
234 FileUtils.copyInputStreamToFile(result, mergedOutFile);
235 } catch (IOException e) {
236 // TODO Auto-generated catch block
245 * Merge and read the prototype bindsings with each tenant specific bindings delta to create the final
248 * @param protoBindingsFile - The prototypical bindings file.
249 * @param tenantDirList - The list of tenant directories containing tenant specific bindings
250 * @return A list of tenant bindings.
251 * @throws IOException Signals that an I/O exception has occurred.
253 List<TenantBindingType> readTenantConfigs(File protoBindingsDir, List<File> tenantDirList, boolean useAppGeneratedBindings) throws IOException {
254 List<TenantBindingType> result = new ArrayList<TenantBindingType>();
257 // Iterate through a list of directories.
259 for (File tenantDir : tenantDirList) {
260 boolean found = false;
261 String errMessage = null;
263 File tenantBindingsProtoFile = null;
264 String tenantName = tenantDir.getName(); // By convention, the directory name should be the tenant name
265 if (useAppGeneratedBindings == true) {
266 tenantBindingsProtoFile = new File(protoBindingsDir.getAbsolutePath() + File.separator + tenantName +
267 "-" + JEEServerDeployment.TENANT_BINDINGS_PROTOTYPE_FILENAME);
269 tenantBindingsProtoFile = new File(protoBindingsDir + File.separator +
270 JEEServerDeployment.TENANT_BINDINGS_PROTOTYPE_FILENAME);
273 if (tenantBindingsProtoFile.exists() == true) {
274 File configFile = new File(tenantDir.getAbsoluteFile() + File.separator + getFileName(tenantName, useAppGeneratedBindings));
275 if (configFile.exists() == true) {
276 InputStream tenantBindingsStream = this.merge(tenantBindingsProtoFile, configFile);
277 TenantBindingConfig tenantBindingConfig = null;
279 tenantBindingConfig = (TenantBindingConfig) parse(tenantBindingsStream,
280 TenantBindingConfig.class);
281 } catch (Exception e) {
282 logger.error("Could not parse the merged tenant bindings.", e);
284 if (tenantBindingConfig != null) {
285 TenantBindingType binding = tenantBindingConfig.getTenantBinding();
286 if (binding != null) {
289 if (logger.isInfoEnabled() == true) {
290 logger.info("Parsed tenant configureation for: " + binding.getDisplayName());
293 errMessage = "Cound not parse the tentant bindings in: ";
296 errMessage = "Could not parse the tenant bindings file: ";
299 errMessage = "Expected to, but could not, find the tenant delta configuration file: " + configFile.getAbsolutePath();
302 errMessage = "Expected to, but could not, find the tenant proto configuration file: " + tenantBindingsProtoFile.getAbsolutePath();
305 if (found == false) {
306 if (logger.isErrorEnabled() == true) {
307 errMessage = errMessage != null ? errMessage : TENANT_BINDINGS_ERROR;
308 logger.error(errMessage + tenantName);
316 private void readDomains(TenantBindingType tenantBinding) throws Exception {
317 for (RepositoryDomainType domain : tenantBinding.getRepositoryDomain()) {
318 String key = getTenantQualifiedIdentifier(tenantBinding.getId(),
320 domains.put(key, domain);
324 private void readServiceBindings(TenantBindingType tenantBinding) throws Exception {
325 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
326 String key = getTenantQualifiedServiceName(tenantBinding.getId(),
327 serviceBinding.getName());
328 serviceBindings.put(key, serviceBinding);
330 if (serviceBinding!=null){
331 ServiceObjectType objectType = serviceBinding.getObject();
332 if (objectType!=null){
333 String docType = objectType.getName();
334 String docTypeKey = getTenantQualifiedIdentifier(tenantBinding.getId(), docType);
335 docTypes.put(docTypeKey, serviceBinding);
338 if (logger.isTraceEnabled()) {
339 logger.trace("readServiceBindings() added service "
341 + " workspace=" + serviceBinding.getName());
347 public List<TenantBindingType> getConfiguration() {
348 return tenantBindingTypeList;
352 * getTenantBindings returns all the tenant bindings read from configuration
355 public Hashtable<String, TenantBindingType> getTenantBindings() {
356 return tenantBindings;
360 * getTenantBinding gets tenant binding for given tenant
364 public TenantBindingType getTenantBinding(
366 return tenantBindings.get(tenantId);
370 * getRepositoryDomain gets repository domain configuration for the given name
374 public RepositoryDomainType getRepositoryDomain(String domainName) {
375 return domains.get(domainName.trim());
379 * getRepositoryDomain gets repository domain configuration for the given service
380 * and given tenant id
385 public RepositoryDomainType getRepositoryDomain(String tenantId, String serviceName) {
386 ServiceBindingType serviceBinding = getServiceBinding(tenantId, serviceName);
387 if (serviceBinding == null) {
388 throw new IllegalArgumentException("no service binding found for " + serviceName
389 + " of tenant with id=" + tenantId);
391 String repoDomain = serviceBinding.getRepositoryDomain();
392 if (repoDomain == null) {
393 if (logger.isTraceEnabled()) {
394 logger.trace("No repository domain configured for " + serviceName
395 + " of tenant with id=" + tenantId);
399 String key = this.getTenantQualifiedIdentifier(tenantId, repoDomain.trim());
400 return domains.get(key);
404 * getServiceBinding gets service binding for given tenant for a given service
409 public ServiceBindingType getServiceBinding(
410 String tenantId, String serviceName) {
411 String key = getTenantQualifiedServiceName(tenantId, serviceName);
412 return serviceBindings.get(key);
416 * getServiceBinding gets service binding for given tenant for a given service
421 public ServiceBindingType getServiceBindingForDocType (String tenantId, String docType) {
422 String key = getTenantQualifiedIdentifier(tenantId, docType);
423 return docTypes.get(key);
427 * getServiceBinding gets service binding for given tenant for a given service
432 public List<ServiceBindingType> getServiceBindingsByType(
433 String tenantId, String serviceType) {
434 List<String> serviceTypes = new ArrayList<String>(1);
435 serviceTypes.add(serviceType);
436 return getServiceBindingsByType(tenantId, serviceTypes);
439 * getServiceBindingsByType gets service bindings for a given tenant
440 * for the services that fall within a supplied set of service type(s)
442 * @param serviceTypes
445 public List<ServiceBindingType> getServiceBindingsByType(
446 String tenantId, List<String> serviceTypes) {
447 ArrayList<ServiceBindingType> list = null;
448 TenantBindingType tenant = tenantBindings.get(tenantId);
449 if (tenant != null) {
450 for (ServiceBindingType sb : tenant.getServiceBindings()) {
451 if (serviceTypes.contains(sb.getType())) {
453 list = new ArrayList<ServiceBindingType>();
465 * @return the properly qualified service name
467 public static String getTenantQualifiedServiceName(
468 String tenantId, String serviceName) {
469 // return tenantId + "." + serviceName.toLowerCase();
470 return getTenantQualifiedIdentifier(tenantId, serviceName.toLowerCase());
473 public static String getTenantQualifiedIdentifier(String tenantId, String identifier) {
474 return tenantId + "." + identifier;
477 * Sets properties in the passed list on the local properties for this TenantBinding.
478 * Note: will only set properties not already set on the TenantBinding.
481 * @param propagateToServices If true, recurses to set set properties
482 * on the associated services.
484 public void setDefaultPropertiesOnTenants(List<PropertyItemType> propList,
485 boolean propagateToServices) {
486 // For each tenant, set properties in list that are not already set
487 if (propList == null || propList.isEmpty()) {
490 for (TenantBindingType tenant : tenantBindings.values()) {
491 for (PropertyItemType prop : propList) {
492 TenantBindingUtils.setPropertyValue(tenant,
493 prop, TenantBindingUtils.SET_PROP_IF_MISSING);
495 if (propagateToServices) {
496 TenantBindingUtils.propagatePropertiesToServices(tenant,
497 TenantBindingUtils.SET_PROP_IF_MISSING);
502 public String getResourcesDir(){
503 return getConfigRootDir() + File.separator + RESOURCES_DIR_NAME;
508 * Returns a list of tenant identifiers (tenant IDs).
510 * @return a list of tenant IDs
512 public List<String> getTenantIds() {
513 List<String> tenantIds = new ArrayList<String>();
515 Hashtable<String, TenantBindingType> tenantBindings = getTenantBindings();
516 if (tenantBindings != null && !tenantBindings.isEmpty()) {
517 Enumeration keys = tenantBindings.keys();
518 while (keys.hasMoreElements()) {
519 tenantId = (String) keys.nextElement();
520 if (Tools.notBlank(tenantId)) {
521 tenantIds.add(tenantId);