]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
c60f8ae4052b6e0a73f6b2f4ac4b754448d83396
[tmp/jakarta-migration.git] /
1 /**
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:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
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.
23  */
24 package org.collectionspace.services.common.config;
25
26 import java.io.File;
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;
34
35 import org.apache.commons.io.FileUtils;
36 import org.collectionspace.services.common.api.JEEServerDeployment;
37 import org.collectionspace.services.common.api.Tools;
38 import org.collectionspace.services.config.service.ServiceBindingType;
39 import org.collectionspace.services.config.service.ServiceObjectType;
40 import org.collectionspace.services.config.tenant.RepositoryDomainType;
41 import org.collectionspace.services.config.tenant.TenantBindingConfig;
42 import org.collectionspace.services.config.tenant.TenantBindingType;
43 import org.collectionspace.services.config.types.PropertyItemType;
44
45 import ch.elca.el4j.services.xmlmerge.Configurer;
46 import ch.elca.el4j.services.xmlmerge.config.AttributeMergeConfigurer;
47 import ch.elca.el4j.services.xmlmerge.config.ConfigurableXmlMerge;
48
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 /**
53  * ServicesConfigReader reads service layer specific configuration
54  * 
55  * $LastChangedRevision: $ $LastChangedDate: $
56  */
57 public class TenantBindingConfigReaderImpl extends AbstractConfigReaderImpl<List<TenantBindingType>> {
58         final public static boolean INCLUDE_CREATE_DISABLED_TENANTS = true;
59         final public static boolean EXCLUDE_CREATE_DISABLED_TENANTS = false;
60
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
63                         + ".delta.xml";
64         final private static String MERGED_SUFFIX = ".merged.xml";
65         private static final String NO_SERVICE_BINDINGS_FOUND_ERR = "No Service bindings found.";
66
67         private static final Logger logger = LoggerFactory.getLogger(TenantBindingConfigReaderImpl.class);
68         private List<TenantBindingType> tenantBindingTypeList;
69         // tenant id, tenant binding, for tenants not marked as createDisabled
70         private Hashtable<String, TenantBindingType> enabledTenantBindings = new Hashtable<String, TenantBindingType>();
71         // tenant id, tenant binding
72         private Hashtable<String, TenantBindingType> allTenantBindings = new Hashtable<String, TenantBindingType>();
73         // repository domains
74         private Hashtable<String, RepositoryDomainType> domains = new Hashtable<String, RepositoryDomainType>();
75         // tenant-qualified servicename, service binding
76         private Hashtable<String, ServiceBindingType> serviceBindings = new Hashtable<String, ServiceBindingType>();
77
78         // tenant-qualified service object name to service name, service binding
79         private Hashtable<String, ServiceBindingType> docTypes = new Hashtable<String, ServiceBindingType>();
80
81         public TenantBindingConfigReaderImpl(String serverRootDir) {
82                 super(serverRootDir);
83         }
84
85         @Override
86         public String getFileName() {
87                 return TENANT_BINDINGS_DELTA_FILENAME;
88         }
89
90         private String getFileName(String tenantName, boolean useAppGeneratedBindings) {
91                 String result = getFileName();
92
93                 if (useAppGeneratedBindings == true) {
94                         result = tenantName + "-" + result;
95                 }
96
97                 return result;
98         }
99
100         protected File getTenantsRootDir() {
101                 File result = null;
102                 String errMessage = null;
103                 try {
104                         String tenantsRootPath = getConfigRootDir() + File.separator
105                                         + JEEServerDeployment.TENANT_BINDINGS_ROOTDIRNAME;
106                         File tenantsRootDir = new File(tenantsRootPath);
107                         if (tenantsRootDir.exists() == true) {
108                                 result = tenantsRootDir;
109                                 if (logger.isDebugEnabled() == true) {
110                                         logger.debug("The home directory for all tenants is at: " + result.getCanonicalPath());
111                                 }
112                         } else {
113                                 errMessage = "The home directory for all tenants is missing or inaccessible: ";
114                                 try {
115                                         errMessage = errMessage + tenantsRootDir.getCanonicalPath();
116                                 } catch (IOException ioException) {
117                                         errMessage = errMessage + tenantsRootDir.getAbsolutePath();
118                                 }
119                         }
120                 } catch (IOException e) {
121                         // Log this exception, but continue anyway. Caller should handle the
122                         // null result gracefully.
123                         logger.equals(e);
124                 }
125
126                 if (errMessage != null) {
127                         logger.error(errMessage);
128                 }
129
130                 return result;
131         }
132
133         /*
134          * Take the directory of the prototype bindings and the directory of the
135          * delta bindings. Merge the two and create (replace) a file named
136          * "tenant-bindings.xml"
137          */
138
139         private InputStream merge(File srcFile, File deltaFile) throws IOException {
140                 InputStream result = null;
141                 try {
142                         FileInputStream srcStream = new FileInputStream(srcFile);
143                         FileInputStream deltaStream = new FileInputStream(deltaFile);
144                         InputStream[] inputStreamArray = { srcStream, deltaStream };
145
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: " + deltaFile.getCanonicalPath(), e);
150                 }
151                 //
152                 // Try to save the merge output to a file that is suffixed with
153                 // ".merged.xml" in the same directory
154                 // as the delta file.
155                 //
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);
161                         try {
162                                 FileUtils.copyInputStreamToFile(result, mergedOutFile);
163                         } catch (IOException e) {
164                                 logger.warn("Could not create a copy of the merged tenant configuration at: " + mergedFileName, e);
165                         }
166                         result.reset(); // reset the stream even if the file create failed.
167                 }
168
169                 return result;
170         }
171
172         @Override
173         public void read(boolean useAppGeneratedBindings) throws Exception {
174                 String tenantsRootPath = getTenantsRootDir().getAbsolutePath();
175                 read(tenantsRootPath, useAppGeneratedBindings);
176         }
177
178         @Override
179         public void read(String tenantRootDirPath, boolean useAppGeneratedBindings) throws Exception {
180                 File tenantsRootDir = new File(tenantRootDirPath);
181                 if (tenantsRootDir.exists() == false) {
182                         throw new Exception("Cound not find tenant bindings root directory: " + tenantRootDirPath);
183                 }
184
185                 List<File> tenantDirs = getDirectories(tenantsRootDir);
186                 tenantBindingTypeList = readTenantConfigs(new File(tenantRootDirPath), tenantDirs, useAppGeneratedBindings);
187                 if (tenantBindingTypeList == null || tenantBindingTypeList.size() < 1) {
188                         throw new Exception(NO_SERVICE_BINDINGS_FOUND_ERR);
189                 }
190
191                 for (TenantBindingType tenantBinding : tenantBindingTypeList) {
192                         if (allTenantBindings.get(tenantBinding.getId()) != null) {
193                                 TenantBindingType tenantBindingOld = allTenantBindings.get(tenantBinding.getId());
194                                 logger.error("Ignoring duplicate binding definition for tenant id=" + tenantBinding.getId()
195                                                 + " existing name=" + tenantBindingOld.getName() + " conflicting (ignored) name="
196                                                 + tenantBinding.getName());
197                                 continue;
198                         }
199                         allTenantBindings.put(tenantBinding.getId(), tenantBinding);
200                         if (!tenantBinding.isCreateDisabled()) {
201                                 enabledTenantBindings.put(tenantBinding.getId(), tenantBinding);
202                         } else {
203                                 logger.warn(String.format("The tenant '%s':'%s' is marked as disabled in its bindings file.",
204                                                 tenantBinding.getName(), tenantBinding.getId()));
205                         }
206                         readDomains(tenantBinding);
207                         readServiceBindings(tenantBinding);
208                         if (logger.isInfoEnabled()) {
209                                 logger.info("Finished reading tenant bindings for tenant id=" + tenantBinding.getId() + " name="
210                                                 + tenantBinding.getName());
211                                 if (tenantBinding.isCreateDisabled())
212                                         logger.info("Tenant tenant id={} is marked createDisabled.", tenantBinding.getId());
213                         }
214                 }
215                 
216                 //
217                 // Ensure that at least one tenant is enabled, otherwise abort the startup.
218                 //
219                 if (enabledTenantBindings.isEmpty() == true) {
220                         throw new Exception("All of the configured tenants are marked as disabled in their tenant bindings.  At least one tenant needs to be enabled.");
221                 }
222         }
223
224         /*
225          * Take the directory of the prototype bindings and the directory of the
226          * delta bindings. Merge the two and create (replace) a file named
227          * "tenant-bindings.xml"
228          * 
229          * private static String merge(String original, String patch) { InputStream
230          * result = null; try { Configurer configurer = new
231          * AttributeMergeConfigurer();
232          * 
233          * 
234          * FileInputStream ins1 = new
235          * FileInputStream(".\\src\\main\\resources\\File1.xml"); FileInputStream
236          * ins2 = new FileInputStream(".\\src\\main\\resources\\File2.xml");
237          * InputStream[] inputStreamArray = {ins1, ins2};
238          * 
239          * result = new ConfigurableXmlMerge(configurer).merge(inputStreamArray); //
240          * result = new ConfigurableXmlMerge(configurer).merge(new String[]
241          * {original, patch}); } catch (Exception e) { // TODO Auto-generated catch
242          * block e.printStackTrace(); } File mergedOutFile = new
243          * File(".\\target\\merged.xml"); try {
244          * FileUtils.copyInputStreamToFile(result, mergedOutFile); } catch
245          * (IOException e) { // TODO Auto-generated catch block e.printStackTrace();
246          * }
247          * 
248          * return null; }
249          */
250
251         /**
252          * Merge and read the prototype bindsings with each tenant specific bindings
253          * delta to create the final tenant bindings.
254          * 
255          * @param protoBindingsFile
256          *            - The prototypical bindings file.
257          * @param tenantDirList
258          *            - The list of tenant directories containing tenant specific
259          *            bindings
260          * @return A list of tenant bindings.
261          * @throws IOException
262          *             Signals that an I/O exception has occurred.
263          */
264         List<TenantBindingType> readTenantConfigs(File protoBindingsDir, List<File> tenantDirList,
265                         boolean useAppGeneratedBindings) throws IOException {
266                 List<TenantBindingType> result = new ArrayList<TenantBindingType>();
267
268                 //
269                 // Iterate through a list of directories.
270                 //
271                 for (File tenantDir : tenantDirList) {
272                         boolean found = false;
273                         String errMessage = null;
274
275                         File tenantBindingsProtoFile = null;
276                         String tenantName = tenantDir.getName();        // By convention, the
277                                                                                                                 // directory name should
278                                                                                                                 // be the tenant name
279                         if (useAppGeneratedBindings == true) {
280                                 tenantBindingsProtoFile = new File(protoBindingsDir.getAbsolutePath() + File.separator + tenantName
281                                                 + "-" + JEEServerDeployment.TENANT_BINDINGS_PROTOTYPE_FILENAME);
282                         } else {
283                                 tenantBindingsProtoFile = new File(protoBindingsDir + File.separator
284                                                 + JEEServerDeployment.TENANT_BINDINGS_PROTOTYPE_FILENAME);
285                         }
286
287                         if (tenantBindingsProtoFile.exists() == true) {
288                                 File configFile = new File(tenantDir.getAbsoluteFile() + File.separator
289                                                 + getFileName(tenantName, useAppGeneratedBindings));
290                                 if (configFile.exists() == true) {
291                                         InputStream tenantBindingsStream = this.merge(tenantBindingsProtoFile, configFile);
292                                         TenantBindingConfig tenantBindingConfig = null;
293                                         try {
294                                                 tenantBindingConfig = (TenantBindingConfig) parse(tenantBindingsStream,
295                                                                 TenantBindingConfig.class);
296                                         } catch (Exception e) {
297                                                 logger.error("Could not parse the merged tenant bindings.", e);
298                                         }
299                                         if (tenantBindingConfig != null) {
300                                                 TenantBindingType binding = tenantBindingConfig.getTenantBinding();
301                                                 if (binding != null) {
302                                                         result.add(binding);
303                                                         found = true;
304                                                         if (logger.isInfoEnabled() == true) {
305                                                                 logger.info("Parsed tenant configuration for: " + binding.getDisplayName());
306                                                         }
307                                                 } else {
308                                                         errMessage = "Cound not parse the tenant bindings in: ";
309                                                 }
310                                         } else {
311                                                 errMessage = "Could not parse the tenant bindings file: ";
312                                         }
313                                 } else {
314                                         errMessage = "Expected to, but could not, find the tenant delta configuration file: "
315                                                         + configFile.getAbsolutePath();
316                                 }
317                         } else {
318                                 errMessage = "Expected to, but could not, find the tenant proto configuration file: "
319                                                 + tenantBindingsProtoFile.getAbsolutePath();
320                         }
321
322                         if (found == false) {
323                                 if (logger.isErrorEnabled() == true) {
324                                         errMessage = errMessage != null ? errMessage : TENANT_BINDINGS_ERROR;
325                                         logger.error(errMessage + " - For tenant: " + tenantName);
326                                 }
327                         }
328                 } // else-for
329
330                 return result;
331         }
332
333         private void readDomains(TenantBindingType tenantBinding) throws Exception {
334                 for (RepositoryDomainType domain : tenantBinding.getRepositoryDomain()) {
335                         String key = getTenantQualifiedIdentifier(tenantBinding.getId(), domain.getName());
336                         domains.put(key, domain);
337                 }
338         }
339
340         private void readServiceBindings(TenantBindingType tenantBinding) throws Exception {
341                 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
342                         String key = getTenantQualifiedServiceName(tenantBinding.getId(), serviceBinding.getName());
343                         if (key == null) {
344                                 continue;
345                         }
346                         serviceBindings.put(key, serviceBinding);
347
348                         if (serviceBinding != null) {
349                                 ServiceObjectType objectType = serviceBinding.getObject();
350                                 if (objectType != null) {
351                                         String docType = objectType.getName();
352                                         String docTypeKey = getTenantQualifiedIdentifier(tenantBinding.getId(), docType);
353                                         docTypes.put(docTypeKey, serviceBinding);
354                                 }
355                         }
356                         if (logger.isTraceEnabled()) {
357                                 logger.trace("readServiceBindings() added service " + " name=" + key + " workspace="
358                                                 + serviceBinding.getName());
359                         }
360                 }
361         }
362
363         @Override
364         public List<TenantBindingType> getConfiguration() {
365                 return tenantBindingTypeList;
366         }
367
368         /**
369          * getTenantBindings returns all the tenant bindings read from configuration
370          * 
371          * @return
372          */
373         public Hashtable<String, TenantBindingType> getTenantBindings() {
374                 return getTenantBindings(EXCLUDE_CREATE_DISABLED_TENANTS);
375         }
376
377         /**
378          * getTenantBindings returns all the tenant bindings read from configuration
379          * 
380          * @return
381          */
382         public Hashtable<String, TenantBindingType> getTenantBindings(boolean includeDisabled) {
383                 return includeDisabled ? allTenantBindings : enabledTenantBindings;
384         }
385
386         /**
387          * getTenantBinding gets tenant binding for given tenant
388          * 
389          * @param tenantId
390          * @return
391          */
392         public TenantBindingType getTenantBinding(String tenantId) {
393                 return allTenantBindings.get(tenantId);
394         }
395
396         /**
397          * getRepositoryDomain gets repository domain configuration for the given
398          * name
399          * 
400          * @param domainName
401          * @return
402          */
403         public RepositoryDomainType getRepositoryDomain(String domainName) {
404                 return domains.get(domainName.trim());
405         }
406
407         /**
408          * getRepositoryDomain gets repository domain configuration for the given
409          * service and given tenant id
410          * 
411          * @param tenantId
412          * @param serviceName
413          * @return
414          */
415         public RepositoryDomainType getRepositoryDomain(String tenantId, String serviceName) {
416                 ServiceBindingType serviceBinding = getServiceBinding(tenantId, serviceName);
417                 if (serviceBinding == null) {
418                         throw new IllegalArgumentException("no service binding found for " + serviceName + " of tenant with id="
419                                         + tenantId);
420                 }
421                 String repoDomain = serviceBinding.getRepositoryDomain();
422                 if (repoDomain == null) {
423                         if (logger.isTraceEnabled()) {
424                                 logger.trace("No repository domain configured for " + serviceName + " of tenant with id=" + tenantId);
425                         }
426                         return null;
427                 }
428                 String key = this.getTenantQualifiedIdentifier(tenantId, repoDomain.trim());
429                 return domains.get(key);
430         }
431
432         /**
433          * getServiceBinding gets service binding for given tenant for a given
434          * service
435          * 
436          * @param tenantId
437          * @param serviceName
438          * @return
439          */
440         public ServiceBindingType getServiceBinding(String tenantId, String serviceName) {
441                 String key = getTenantQualifiedServiceName(tenantId, serviceName);
442                 return serviceBindings.get(key);
443         }
444
445         /**
446          * getServiceBinding gets service binding for given tenant for a given
447          * service
448          * 
449          * @param tenantId
450          * @param docType
451          * @return
452          */
453         public ServiceBindingType getServiceBindingForDocType(String tenantId, String docType) {
454                 String key = getTenantQualifiedIdentifier(tenantId, docType);
455                 return docTypes.get(key);
456         }
457
458         /**
459          * getServiceBinding gets service binding for given tenant for a given
460          * service
461          * 
462          * @param tenantId
463          * @param serviceName
464          * @return
465          */
466         public List<ServiceBindingType> getServiceBindingsByType(String tenantId, String serviceType) {
467                 List<String> serviceTypes = new ArrayList<String>(1);
468                 serviceTypes.add(serviceType);
469                 return getServiceBindingsByType(tenantId, serviceTypes);
470         }
471
472         /**
473          * getServiceBindingsByType gets service bindings for a given tenant for the
474          * services that fall within a supplied set of service type(s)
475          * 
476          * @param tenantId
477          * @param serviceTypes
478          * @return
479          */
480         public List<ServiceBindingType> getServiceBindingsByType(String tenantId, List<String> serviceTypes) {
481                 ArrayList<ServiceBindingType> list = null;
482                 TenantBindingType tenant = allTenantBindings.get(tenantId);
483                 if (tenant != null) {
484                         for (ServiceBindingType sb : tenant.getServiceBindings()) {
485                                 if (serviceTypes.contains(sb.getType())) {
486                                         if (list == null) {
487                                                 list = new ArrayList<ServiceBindingType>();
488                                         }
489                                         list.add(sb);
490                                 }
491                         }
492                 }
493                 return list;
494         }
495
496         /**
497          * @param tenantId
498          * @param serviceName
499          * @return the properly qualified service name
500          */
501         public static String getTenantQualifiedServiceName(String tenantId, String serviceName) {
502                 String result = null;
503
504                 if (serviceName != null) {
505                         if (logger.isDebugEnabled() == true) {
506                                 logger.debug(String.format(" * tenant:serviceBindings '%s'", serviceName));
507                                 //System.out.println(String.format(" * tenant:serviceBindings '%s'", serviceName)); // Debug only
508                         }
509                         result = getTenantQualifiedIdentifier(tenantId, serviceName.toLowerCase());
510                 }
511
512                 return result;
513         }
514
515         public static String getTenantQualifiedIdentifier(String tenantId, String identifier) {
516                 return tenantId + "." + identifier;
517         }
518
519         /**
520          * Sets properties in the passed list on the local properties for this
521          * TenantBinding. Note: will only set properties not already set on the
522          * TenantBinding.
523          * 
524          * @param propList
525          * @param propagateToServices
526          *            If true, recurses to set set properties on the associated
527          *            services.
528          */
529         public void setDefaultPropertiesOnTenants(List<PropertyItemType> propList, boolean propagateToServices) {
530                 // For each tenant, set properties in list that are not already set
531                 if (propList == null || propList.isEmpty()) {
532                         return;
533                 }
534                 for (TenantBindingType tenant : allTenantBindings.values()) {
535                         for (PropertyItemType prop : propList) {
536                                 TenantBindingUtils.setPropertyValue(tenant, prop, TenantBindingUtils.SET_PROP_IF_MISSING);
537                         }
538                         if (propagateToServices) {
539                                 TenantBindingUtils.propagatePropertiesToServices(tenant, TenantBindingUtils.SET_PROP_IF_MISSING);
540                         }
541                 }
542         }
543
544         public String getResourcesDir() {
545                 return getConfigRootDir() + File.separator + RESOURCES_DIR_NAME;
546         }
547
548         /**
549          * Returns a list of tenant identifiers (tenant IDs).
550          * 
551          * @return a list of tenant IDs
552          */
553         public List<String> getTenantIds() {
554                 return getTenantIds(EXCLUDE_CREATE_DISABLED_TENANTS);
555         }
556
557         /**
558          * Returns a list of tenant identifiers (tenant IDs).
559          * 
560          * @return a list of tenant IDs
561          */
562         public List<String> getTenantIds(boolean includeDisabled) {
563                 List<String> tenantIds = new ArrayList<String>();
564                 String tenantId;
565                 Hashtable<String, TenantBindingType> tenantBindings = getTenantBindings(includeDisabled);
566                 if (tenantBindings != null && !tenantBindings.isEmpty()) {
567                         Enumeration keys = tenantBindings.keys();
568                         while (keys.hasMoreElements()) {
569                                 tenantId = (String) keys.nextElement();
570                                 if (Tools.notBlank(tenantId)) {
571                                         tenantIds.add(tenantId);
572                                 }
573                         }
574                 }
575                 return tenantIds;
576         }
577 }