001/**
002 * Copyright 2005-2015 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.bo;
017
018import org.apache.commons.beanutils.MethodUtils;
019import org.apache.commons.lang.builder.ToStringBuilder;
020import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
021import org.kuali.rice.krad.data.KradDataServiceLocator;
022import org.kuali.rice.krad.data.provider.MetadataProvider;
023import org.kuali.rice.krad.data.provider.Provider;
024import org.kuali.rice.krad.data.provider.ProviderRegistry;
025import org.kuali.rice.krad.service.DataDictionaryService;
026import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
027import org.springframework.beans.factory.InitializingBean;
028import org.springframework.context.ApplicationContext;
029import org.springframework.context.ApplicationContextAware;
030
031
032import java.lang.reflect.Modifier;
033import java.util.ArrayList;
034import java.util.Collections;
035import java.util.List;
036import java.util.Map;
037
038/**
039 * This class contains various configuration properties for a Rice module.
040 *
041 * <p>
042 * The Rice framework is  composed of several separate modules, each of which is
043 * responsible for providing a set of functionality. These include:
044 * <ul>
045 *      <li>KEW - the Rice enterprise workflow module
046 *      <li>KIM - the Rice identity management module
047 *      <li>KSB - the Rice service bus
048 *      <li>KRAD - the Rice rapid application development module
049 *      <li>KRMS - the Rice business rules management syste
050 *      <li>eDocLite - a Rice framework for creating simple documents quickly
051 *      <li>...as well as several others. Refer to the Rice documentation for a complete list.
052 * </ul>
053 * <br>
054 * Client Applications will also have their own module configurations. A client application could create a single
055 * module or multiple modules, depending on how it is organized.
056 * <br>
057 * This ModuleConfiguration object is created during Spring initialization. The properties of this ModuleConfiguration
058 * are specified in the module's SpringBean definition XML configuration file.
059 *</p>
060 *
061 * @author Kuali Rice Team (rice.collab@kuali.org)
062 *
063 */
064public class ModuleConfiguration implements InitializingBean, ApplicationContextAware {
065    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ModuleConfiguration.class);
066    /**
067     * the module's namespace.
068     */
069        protected String namespaceCode;
070        protected ApplicationContext applicationContext;
071
072    /**
073     * the package name prefixes for classes used in this module
074     */
075        protected List<String> packagePrefixes;
076
077    /**
078     * a list of entity description files to be loaded during initialization of the persistence service.
079     * <p>
080     * Currently only used by OJB repository service implementation.
081     * </p>
082     */
083        protected List<String> databaseRepositoryFilePaths;
084
085    /**
086     * the list of data dictionary packages to be loaded for this module by the data dictionary service during system
087     * startup.
088     */
089        protected List<String> dataDictionaryPackages;
090
091        protected List<String> scriptConfigurationFilePaths;
092
093    protected List<String> resourceBundleNames;
094
095        //optional
096        protected String dataSourceName;
097
098        protected Map<Class, Class> externalizableBusinessObjectImplementations;
099
100        protected boolean initializeDataDictionary;
101
102        protected Object persistenceService;
103
104        protected ProviderRegistry providerRegistry;
105        
106    /**
107     * the implementation of the data dictionary service to use for this module.
108     */
109        protected DataDictionaryService dataDictionaryService;
110
111    protected List<Provider> providers = Collections.unmodifiableList(Collections.<Provider>emptyList());
112
113    /**
114     *  Constructor for a ModuleConfiguration.
115     *
116     *  <p>
117     *  Initializes the arrays of this ModuleConfiguration to empty ArrayLists.
118     *  </p>
119     */
120        public ModuleConfiguration() {
121                databaseRepositoryFilePaths = new ArrayList<String>();
122                dataDictionaryPackages = new ArrayList<String>();
123                scriptConfigurationFilePaths = new ArrayList<String>();
124        resourceBundleNames = new ArrayList<String>();
125        }
126
127    /**
128     * Performs additional custom initialization after the bean is created and it's properties are set by the
129     * Spring framework.
130     *
131     * <p>
132     * Loads the data dictionary packages configured for this module.
133     * Also loads any OJB database repository files configured.
134     * </p>
135     *
136     * @throws Exception
137     */
138        @Override
139    public void afterPropertiesSet() throws Exception {
140        if (isInitializeDataDictionary() && getDataDictionaryPackages() != null &&
141                !getDataDictionaryPackages().isEmpty()) {
142            if (getDataDictionaryService() == null) {
143                setDataDictionaryService(KRADServiceLocatorWeb.getDataDictionaryService());
144            }
145
146            if (getDataDictionaryService() == null) {
147                setDataDictionaryService((DataDictionaryService) applicationContext.getBean(
148                        KRADServiceLocatorWeb.DATA_DICTIONARY_SERVICE));
149            }
150
151            if (dataDictionaryService != null) {
152                dataDictionaryService.addDataDictionaryLocations(getNamespaceCode(), getDataDictionaryPackages());
153            }
154        }
155
156        loadOjbRepositoryFiles();
157
158        if ( getProviders() != null ) {
159            ProviderRegistry providerRegistry = getProviderRegistry();
160            if ( providerRegistry != null ) {
161                for ( Provider provider : getProviders() ) {
162                    LOG.info( "Registering data module provider for module with " + getNamespaceCode() + ": " + provider);
163                    providerRegistry.registerProvider(provider);
164                }
165            } else {
166                LOG.error( "Provider registry not initialized.  Data module provider configuration will be incomplete. (" + getNamespaceCode() + ")" );
167            }
168        }
169    }
170
171    /**
172     * This method is deprecated and won't do anything if the database repository file paths are null or empty.
173     *
174     * We use reflection here to avoid having to reference PersistenceService directly since it may or may not be on
175     * our classpath depending on whether or not KSB is in use.
176     */
177    @Deprecated
178    protected void loadOjbRepositoryFiles() {
179        String persistenceServiceOjbName = "persistenceServiceOjb";
180        if (getDatabaseRepositoryFilePaths() != null) {
181            for (String repositoryLocation : getDatabaseRepositoryFilePaths()) {
182                // Need the OJB persistence service because it is the only one ever using the database repository files
183                if (getPersistenceService() == null) {
184                    setPersistenceService(GlobalResourceLoader.getService(persistenceServiceOjbName));
185                }
186                if (persistenceService == null) {
187                    setPersistenceService(applicationContext.getBean(persistenceServiceOjbName));
188                }
189                LOG.warn("Loading OJB Configuration in "
190                        + getNamespaceCode()
191                        + " module.  OJB is deprecated as of Rice 2.4: "
192                        + repositoryLocation);
193                try {
194                    MethodUtils.invokeExactMethod(persistenceService, "loadRepositoryDescriptor", repositoryLocation);
195                } catch (Exception e) {
196                    throw new RuntimeException(e);
197                }
198            }
199        }
200
201    }
202
203        /**
204     * Retrieves the database repository file paths to be used by the persistence service configured for this module.
205     *
206     * <p>
207     * Used by the OBJ persistence service to load entity descriptors.
208     * The file paths are returned as a List of Strings. If no file paths are configured,
209     * an empty list is returned.  This method should never return null.
210     * </p>
211     *
212         * @return a List containing the databaseRepositoryFilePaths
213     *
214     * @deprecated OJB is deprecated
215         */
216    @Deprecated
217        public List<String> getDatabaseRepositoryFilePaths() {
218                return this.databaseRepositoryFilePaths;
219        }
220
221        /**
222     * Initializes the list of database repository files to load during persistence service initialization.
223     *
224     * <p>
225     * The repository file names are listed in the module's Spring bean configuration file.
226     * This property is set during Spring initialization.
227     * </p>
228     *
229         * @param databaseRepositoryFilePaths the List of entity descriptor files to load.
230     *
231     * @deprecated OJB is deprecated
232     */
233    @Deprecated
234        public void setDatabaseRepositoryFilePaths(
235                        List<String> databaseRepositoryFilePaths) {
236                this.trimList(databaseRepositoryFilePaths);
237                this.databaseRepositoryFilePaths = databaseRepositoryFilePaths;
238        }
239
240        /**
241     * Returns a list of data dictionary packages configured for this ModuleConfiguration.
242     *
243     * <p>
244     * If no data dictionary packages are defined, will return an empty list.
245     * Should never return null.
246     * </p>
247     *
248         * @return a List of Strings containing the names of the dataDictionaryPackages
249         */
250        public List<String> getDataDictionaryPackages() {
251                return this.dataDictionaryPackages;
252        }
253
254        /**
255     * Initializes the list of data dictionary packages associated with this ModuleConfiguration.
256     *
257     * <p>
258     * The data dictionary packages are listed in the module's Spring bean configuration file.
259     * This property is set during Spring initialization.
260     * </p>
261     *
262         * @param dataDictionaryPackages a List of Strings containing the dataDictionaryPackages.
263         */
264        public void setDataDictionaryPackages(List<String> dataDictionaryPackages) {
265                this.trimList(dataDictionaryPackages);
266                this.dataDictionaryPackages = dataDictionaryPackages;
267        }
268
269        /**
270         * @return the externalizableBusinessObjectImplementations
271         */
272        public Map<Class, Class> getExternalizableBusinessObjectImplementations() {
273                if (this.externalizableBusinessObjectImplementations == null) {
274            return null;
275        }
276                return Collections.unmodifiableMap(this.externalizableBusinessObjectImplementations);
277        }
278
279        /**
280         * @param externalizableBusinessObjectImplementations the externalizableBusinessObjectImplementations to set
281         */
282        public void setExternalizableBusinessObjectImplementations(
283                        Map<Class, Class> externalizableBusinessObjectImplementations) {
284                if (externalizableBusinessObjectImplementations != null) {
285                        for (Class implClass : externalizableBusinessObjectImplementations.values()) {
286                                int implModifiers = implClass.getModifiers();
287                                if (Modifier.isInterface(implModifiers) || Modifier.isAbstract(implModifiers)) {
288                                        throw new RuntimeException("Externalizable business object implementation class " +
289                                                        implClass.getName() + " must be a non-interface, non-abstract class");
290                                }
291                        }
292                }
293                this.externalizableBusinessObjectImplementations = externalizableBusinessObjectImplementations;
294        }
295
296        public List<String> getPackagePrefixes(){
297                return this.packagePrefixes;
298        }
299
300        public void setPackagePrefixes(List<String> packagePrefixes){
301                this.trimList(packagePrefixes);
302                this.packagePrefixes = packagePrefixes;
303        }
304
305        public void setInitializeDataDictionary(boolean initializeDataDictionary){
306                this.initializeDataDictionary = initializeDataDictionary;
307        }
308
309        public List<String> getScriptConfigurationFilePaths(){
310                return this.scriptConfigurationFilePaths;
311        }
312
313    /**
314     * List of resource bundle names that will provides messages for this module
315     *
316     * <p>
317     * Each bundle will point to a resource property file that contain key/value message pairs. The properties
318     * file should be on the classpath and the name is given by specifying the fully qualified class name
319     * (dot notation).
320     * </p>
321     *
322     * @return List<String> resource bundle names
323     * @see java.util.ResourceBundle
324     */
325    public List<String> getResourceBundleNames() {
326        return resourceBundleNames;
327    }
328
329    /**
330     * Setter for the list of resource bundle names that provides messages for the module
331     *
332     * @param resourceBundleNames
333     */
334    public void setResourceBundleNames(List<String> resourceBundleNames) {
335        this.resourceBundleNames = resourceBundleNames;
336    }
337
338    /**
339         * @return the initializeDataDictionary
340         */
341        public boolean isInitializeDataDictionary() {
342                return this.initializeDataDictionary;
343        }
344
345        /**
346         * @param scriptConfigurationFilePaths the scriptConfigurationFilePaths to set
347         */
348        public void setScriptConfigurationFilePaths(
349                        List<String> scriptConfigurationFilePaths) {
350                this.scriptConfigurationFilePaths = scriptConfigurationFilePaths;
351        }
352
353        /**
354         * @return the namespaceCode
355         */
356        public String getNamespaceCode() {
357                return this.namespaceCode;
358        }
359
360        /**
361         * @param namespaceCode the namespaceCode to set
362         */
363        public void setNamespaceCode(String namespaceCode) {
364                this.namespaceCode = namespaceCode;
365        }
366
367        @Override
368        public void setApplicationContext(ApplicationContext applicationContext) {
369                this.applicationContext = applicationContext;
370        }
371
372    /**
373     * Sets the list of providers for this module
374     * @param providers list of providers
375     */
376    public void setProviders(List<Provider> providers) {
377        this.providers = Collections.unmodifiableList(new ArrayList<Provider>(providers));
378    }
379
380    public List<Provider> getProviders() {
381        return providers;
382    }
383
384        /**
385         * @return the dataDictionaryService
386         */
387        public DataDictionaryService getDataDictionaryService() {
388                return this.dataDictionaryService;
389        }
390
391        /**
392         * @param dataDictionaryService the dataDictionaryService to set
393         */
394        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
395                this.dataDictionaryService = dataDictionaryService;
396        }
397
398        /**
399     * @return the providerRegistry
400     */
401    public ProviderRegistry getProviderRegistry() {
402        if (this.providerRegistry == null) {
403            this.providerRegistry = KradDataServiceLocator.getProviderRegistry();
404        }
405        
406        return this.providerRegistry;
407    }
408
409    /**
410     * @param providerRegistry the providerRegistry to set
411     */
412    public void setProviderRegistry(ProviderRegistry providerRegistry) {
413        this.providerRegistry = providerRegistry;
414    }
415
416    /**
417         * @return the persistenceService
418         */
419    @Deprecated
420        public Object getPersistenceService() {
421                return this.persistenceService;
422        }
423
424        /**
425         * @param persistenceService the persistenceService to set
426         */
427    @Deprecated
428        public void setPersistenceService(Object persistenceService) {
429                this.persistenceService = persistenceService;
430        }
431
432    public String getDataSourceName() {
433        return this.dataSourceName;
434    }
435
436    public void setDataSourceName(String dataSourceName) {
437        this.dataSourceName = dataSourceName;
438    }
439
440    /**
441         *
442         * This method passes by reference. It will alter the list passed in.
443         *
444         * @param stringList
445         */
446        protected void trimList(List<String> stringList){
447                if(stringList != null){
448                        // we need to trim whitespace from the stringList. Because trim() creates a new string
449                        // we have to explicitly put the new string back into the list
450                        for(int i=0; i<stringList.size(); i++){
451                                String elmt = stringList.get(i);
452                                elmt = elmt.trim();
453                                stringList.set(i, elmt);
454                        }
455                }
456        }
457
458    @Override
459    public String toString() {
460        return new ToStringBuilder(this)
461                    .append("namespaceCode", namespaceCode)
462                    .append("applicationContext", applicationContext.getDisplayName())
463                    .append("dataSourceName", dataSourceName)
464                    .toString();
465    }
466
467}