001    /**
002     * Copyright 2005-2011 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     */
016    package org.kuali.rice.krad.service.impl;
017    
018    import java.lang.reflect.Modifier;
019    import java.util.HashMap;
020    import java.util.List;
021    import java.util.Map;
022    import java.util.Map.Entry;
023    import java.util.Properties;
024    
025    import org.apache.commons.beanutils.PropertyUtils;
026    import org.apache.commons.lang.StringUtils;
027    import org.apache.log4j.Logger;
028    import org.kuali.rice.core.framework.parameter.ParameterService;
029    import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
030    import org.kuali.rice.krad.bo.BusinessObject;
031    import org.kuali.rice.krad.bo.DataObjectRelationship;
032    import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
033    import org.kuali.rice.krad.bo.ModuleConfiguration;
034    import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
035    import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
036    import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
037    import org.kuali.rice.krad.service.BusinessObjectDictionaryService;
038    import org.kuali.rice.krad.service.BusinessObjectNotLookupableException;
039    import org.kuali.rice.krad.service.BusinessObjectService;
040    import org.kuali.rice.krad.service.KRADServiceLocator;
041    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
042    import org.kuali.rice.krad.service.KualiModuleService;
043    import org.kuali.rice.krad.service.LookupService;
044    import org.kuali.rice.krad.service.ModuleService;
045    import org.kuali.rice.krad.util.ExternalizableBusinessObjectUtils;
046    import org.kuali.rice.krad.util.KRADConstants;
047    import org.kuali.rice.krad.util.ObjectUtils;
048    import org.kuali.rice.krad.util.UrlFactory;
049    import org.springframework.beans.BeansException;
050    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
051    import org.springframework.context.ApplicationContext;
052    
053    /**
054     * This class implements ModuleService interface.
055     *
056     * @author Kuali Rice Team (rice.collab@kuali.org)
057     */
058    public class ModuleServiceBase implements ModuleService {
059    
060            protected static final Logger LOG = Logger.getLogger(ModuleServiceBase.class);
061    
062            protected ModuleConfiguration moduleConfiguration;
063            protected BusinessObjectService businessObjectService;
064            protected LookupService lookupService;
065            protected BusinessObjectDictionaryService businessObjectDictionaryService;
066            protected KualiModuleService kualiModuleService;
067            protected ApplicationContext applicationContext;
068    
069            /***
070             * @see org.kuali.rice.krad.service.ModuleService#isResponsibleFor(java.lang.Class)
071             */
072            public boolean isResponsibleFor(Class businessObjectClass) {
073                    if(getModuleConfiguration() == null)
074                            throw new IllegalStateException("Module configuration has not been initialized for the module service.");
075    
076                    if (getModuleConfiguration().getPackagePrefixes() == null || businessObjectClass == null) {
077                            return false;
078                    }
079                    for (String prefix : getModuleConfiguration().getPackagePrefixes()) {
080                            if (businessObjectClass.getPackage().getName().startsWith(prefix)) {
081                                    return true;
082                            }
083                    }
084                    if (ExternalizableBusinessObject.class.isAssignableFrom(businessObjectClass)) {
085                            Class externalizableBusinessObjectInterface = ExternalizableBusinessObjectUtils.determineExternalizableBusinessObjectSubInterface(businessObjectClass);
086                            if (externalizableBusinessObjectInterface != null) {
087                                    for (String prefix : getModuleConfiguration().getPackagePrefixes()) {
088                                            if (externalizableBusinessObjectInterface.getPackage().getName().startsWith(prefix)) {
089                                                    return true;
090                                            }
091                                    }
092                            }
093                    }
094                    return false;
095            }
096    
097    
098    
099            /***
100             * @see org.kuali.rice.krad.service.ModuleService#isResponsibleFor(java.lang.Class)
101             */
102            public boolean isResponsibleForJob(String jobName) {
103                    if(getModuleConfiguration() == null)
104                            throw new IllegalStateException("Module configuration has not been initialized for the module service.");
105    
106                    if (getModuleConfiguration().getJobNames() == null || StringUtils.isEmpty(jobName))
107                            return false;
108    
109                    return getModuleConfiguration().getJobNames().contains(jobName);
110            }
111    
112        /***
113         * @see org.kuali.rice.krad.service.ModuleService#getExternalizableBusinessObject(java.lang.Class, java.util.Map)
114         */
115        public <T extends ExternalizableBusinessObject> T getExternalizableBusinessObject(Class<T> businessObjectClass, Map<String, Object> fieldValues) {
116            Class<? extends ExternalizableBusinessObject> implementationClass = getExternalizableBusinessObjectImplementation(businessObjectClass);
117                    ExternalizableBusinessObject businessObject = (ExternalizableBusinessObject)
118                            getBusinessObjectService().findByPrimaryKey(implementationClass, fieldValues);
119            return (T) businessObject;
120            }
121    
122        /***
123         * @see org.kuali.rice.krad.service.ModuleService#getExternalizableBusinessObject(java.lang.Class, java.util.Map)
124         */
125            public <T extends ExternalizableBusinessObject> List<T> getExternalizableBusinessObjectsList(
126                            Class<T> externalizableBusinessObjectClass, Map<String, Object> fieldValues) {
127                    Class<? extends ExternalizableBusinessObject> implementationClass = getExternalizableBusinessObjectImplementation(externalizableBusinessObjectClass);
128                    return (List<T>) getBusinessObjectService().findMatching(implementationClass, fieldValues);
129            }
130    
131            /***
132             * @see org.kuali.rice.krad.service.ModuleService#getExternalizableBusinessObjectsListForLookup(java.lang.Class, java.util.Map, boolean)
133             */
134            public <T extends ExternalizableBusinessObject> List<T> getExternalizableBusinessObjectsListForLookup(
135                            Class<T> externalizableBusinessObjectClass, Map<String, Object> fieldValues, boolean unbounded) {
136                    Class<? extends ExternalizableBusinessObject> implementationClass = getExternalizableBusinessObjectImplementation(externalizableBusinessObjectClass);
137                    if (isExternalizableBusinessObjectLookupable(implementationClass)) {
138                            Map<String, String> searchCriteria = new HashMap<String, String>();
139                            for (Entry<String, Object> fieldValue : fieldValues.entrySet()) {
140                                    if (fieldValue.getValue() != null) {
141                                            searchCriteria.put(fieldValue.getKey(), fieldValue.getValue().toString());
142                                    }
143                                    else {
144                                            searchCriteria.put(fieldValue.getKey(), null);
145                                    }
146                            }
147                        return (List<T>) getLookupService().findCollectionBySearchHelper(implementationClass, searchCriteria, unbounded);
148                    } else {
149                       throw new BusinessObjectNotLookupableException("External business object is not a Lookupable:  " + implementationClass);
150                    }
151            }
152    
153            public List listPrimaryKeyFieldNames(Class businessObjectInterfaceClass){
154                    Class clazz = getExternalizableBusinessObjectImplementation(businessObjectInterfaceClass);
155                    return KRADServiceLocator.getPersistenceStructureService().listPrimaryKeyFieldNames(clazz);
156            }
157    
158            /***
159             * @see org.kuali.rice.krad.service.ModuleService#getExternalizableBusinessObjectDictionaryEntry(java.lang.Class)
160             */
161            public BusinessObjectEntry getExternalizableBusinessObjectDictionaryEntry(
162                            Class businessObjectInterfaceClass) {
163                    Class boClass = getExternalizableBusinessObjectImplementation(businessObjectInterfaceClass);
164    
165                    return boClass==null?null:
166                            KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getBusinessObjectEntryForConcreteClass(boClass.getName());
167            }
168    
169            public String getExternalizableBusinessObjectInquiryUrl(Class inquiryBusinessObjectClass, Map<String, String[]> parameters) {
170                    if(!ExternalizableBusinessObject.class.isAssignableFrom(inquiryBusinessObjectClass)) {
171                    return KRADConstants.EMPTY_STRING;
172                    }
173                    String businessObjectClassAttribute;
174    
175            Class implementationClass = getExternalizableBusinessObjectImplementation(inquiryBusinessObjectClass);
176            if (implementationClass == null) {
177                LOG.error("Can't find ExternalizableBusinessObject implementation class for " + inquiryBusinessObjectClass.getName());
178                throw new RuntimeException("Can't find ExternalizableBusinessObject implementation class for interface " + inquiryBusinessObjectClass.getName());
179            }
180            businessObjectClassAttribute = implementationClass.getName();
181            return UrlFactory.parameterizeUrl(
182                            getInquiryUrl(inquiryBusinessObjectClass),
183                            getUrlParameters(businessObjectClassAttribute, parameters));
184            }
185    
186            protected Properties getUrlParameters(String businessObjectClassAttribute, Map<String, String[]> parameters){
187                    Properties urlParameters = new Properties();
188                    for (String paramName : parameters.keySet()) {
189                            String[] parameterValues = parameters.get(paramName);
190                            if (parameterValues.length > 0) {
191                                    urlParameters.put(paramName, parameterValues[0]);
192                            }
193                    }
194                    urlParameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, businessObjectClassAttribute);
195                    urlParameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.CONTINUE_WITH_INQUIRY_METHOD_TO_CALL);
196                    return urlParameters;
197            }
198    
199            protected String getInquiryUrl(Class inquiryBusinessObjectClass){
200                    String riceBaseUrl = KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
201                    KRADConstants.APPLICATION_URL_KEY);
202                    String inquiryUrl = riceBaseUrl;
203                    if (!inquiryUrl.endsWith("/")) {
204                            inquiryUrl = inquiryUrl + "/";
205                    }
206                    return inquiryUrl + "kr/" + KRADConstants.INQUIRY_ACTION;
207            }
208    
209            /**
210             * This overridden method ...
211             *
212             * @see org.kuali.rice.krad.service.ModuleService#getExternalizableBusinessObjectLookupUrl(java.lang.Class, java.util.Map)
213             */
214            public String getExternalizableBusinessObjectLookupUrl(Class inquiryBusinessObjectClass, Map<String, String> parameters) {
215                    Properties urlParameters = new Properties();
216    
217                    String riceBaseUrl = KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
218                    KRADConstants.APPLICATION_URL_KEY);
219                    String lookupUrl = riceBaseUrl;
220                    if (!lookupUrl.endsWith("/")) {
221                            lookupUrl = lookupUrl + "/";
222                    }
223                    if (parameters.containsKey(KRADConstants.MULTIPLE_VALUE)) {
224                            lookupUrl = lookupUrl + "kr/" + KRADConstants.MULTIPLE_VALUE_LOOKUP_ACTION;
225                    }
226                    else {
227                            lookupUrl = lookupUrl + "kr/" + KRADConstants.LOOKUP_ACTION;
228                    }
229                    for (String paramName : parameters.keySet()) {
230                            urlParameters.put(paramName, parameters.get(paramName));
231                    }
232    
233                    Class clazz = getExternalizableBusinessObjectImplementation(inquiryBusinessObjectClass);
234                    urlParameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, clazz==null?"":clazz.getName());
235    
236                    return UrlFactory.parameterizeUrl(lookupUrl, urlParameters);
237            }
238    
239            /***
240             *
241             * This method assumes that the property type for externalizable relationship in the business object is an interface
242             * and gets the concrete implementation for it
243             *
244             * @see org.kuali.rice.krad.service.ModuleService#retrieveExternalizableBusinessObjectIfNecessary(org.kuali.rice.krad.bo.BusinessObject, org.kuali.rice.krad.bo.BusinessObject, java.lang.String)
245             */
246            public <T extends ExternalizableBusinessObject> T retrieveExternalizableBusinessObjectIfNecessary(
247                            BusinessObject businessObject, T currentInstanceExternalizableBO, String externalizableRelationshipName) {
248    
249                    if(businessObject==null) return null;
250                    Class clazz;
251                    try{
252                            clazz = getExternalizableBusinessObjectImplementation(
253                                            PropertyUtils.getPropertyType(businessObject, externalizableRelationshipName));
254                    } catch(Exception iex){
255                            LOG.warn("Exception:"+iex+" thrown while trying to get property type for property:"+externalizableRelationshipName+
256                                            " from business object:"+businessObject);
257                            return null;
258                    }
259    
260                    //Get the business object entry for this business object from data dictionary
261                    //using the class name (without the package) as key
262                    BusinessObjectEntry entry =
263                            KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getBusinessObjectEntries().get(
264                                            businessObject.getClass().getSimpleName());
265                    RelationshipDefinition relationshipDefinition = entry.getRelationshipDefinition(externalizableRelationshipName);
266                    List<PrimitiveAttributeDefinition> primitiveAttributeDefinitions = relationshipDefinition.getPrimitiveAttributes();
267    
268                    Map<String, Object> fieldValuesInEBO = new HashMap<String, Object>();
269                    Object sourcePropertyValue;
270                    Object targetPropertyValue = null;
271                    boolean sourceTargetPropertyValuesSame = true;
272                    for(PrimitiveAttributeDefinition primitiveAttributeDefinition: primitiveAttributeDefinitions){
273                    sourcePropertyValue = ObjectUtils.getPropertyValue(
274                                    businessObject, primitiveAttributeDefinition.getSourceName());
275                    if(currentInstanceExternalizableBO!=null)
276                            targetPropertyValue = ObjectUtils.getPropertyValue(currentInstanceExternalizableBO, primitiveAttributeDefinition.getTargetName());
277                        if(sourcePropertyValue==null){
278                            return null;
279                        } else if(targetPropertyValue==null || (targetPropertyValue!=null && !targetPropertyValue.equals(sourcePropertyValue))){
280                            sourceTargetPropertyValuesSame = false;
281                        }
282                        fieldValuesInEBO.put(primitiveAttributeDefinition.getTargetName(), sourcePropertyValue);
283                    }
284    
285                    if(!sourceTargetPropertyValuesSame)
286                            return (T) getExternalizableBusinessObject(clazz, fieldValuesInEBO);
287                    return currentInstanceExternalizableBO;
288            }
289    
290            /***
291             *
292             * This method assumes that the externalizableClazz is an interface
293             * and gets the concrete implementation for it
294             *
295             * @see org.kuali.rice.krad.service.ModuleService#retrieveExternalizableBusinessObjectIfNecessary(org.kuali.rice.krad.bo.BusinessObject, org.kuali.rice.krad.bo.BusinessObject, java.lang.String)
296             */
297            public List<? extends ExternalizableBusinessObject> retrieveExternalizableBusinessObjectsList(
298                            BusinessObject businessObject, String externalizableRelationshipName, Class externalizableClazz) {
299    
300                    if(businessObject==null) return null;
301                    //Get the business object entry for this business object from data dictionary
302                    //using the class name (without the package) as key
303                    String className = businessObject.getClass().getName();
304                    String key = className.substring(className.lastIndexOf(".")+1);
305                    BusinessObjectEntry entry =
306                            KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getBusinessObjectEntries().get(key);
307                    RelationshipDefinition relationshipDefinition = entry.getRelationshipDefinition(externalizableRelationshipName);
308                    List<PrimitiveAttributeDefinition> primitiveAttributeDefinitions = relationshipDefinition.getPrimitiveAttributes();
309                    Map<String, Object> fieldValuesInEBO = new HashMap<String, Object>();
310                    Object sourcePropertyValue;
311                    for(PrimitiveAttributeDefinition primitiveAttributeDefinition: primitiveAttributeDefinitions){
312                    sourcePropertyValue = ObjectUtils.getPropertyValue(
313                                    businessObject, primitiveAttributeDefinition.getSourceName());
314                        if(sourcePropertyValue==null){
315                            return null;
316                        }
317                        fieldValuesInEBO.put(primitiveAttributeDefinition.getTargetName(), sourcePropertyValue);
318                    }
319                    return getExternalizableBusinessObjectsList(
320                                    getExternalizableBusinessObjectImplementation(externalizableClazz), fieldValuesInEBO);
321            }
322    
323            /**
324             * @see org.kuali.rice.krad.service.ModuleService#getExternalizableBusinessObjectImplementation(java.lang.Class)
325             */
326            public <E extends ExternalizableBusinessObject> Class<E> getExternalizableBusinessObjectImplementation(Class<E> externalizableBusinessObjectInterface) {
327                    if (getModuleConfiguration() == null) {
328                            throw new IllegalStateException("Module configuration has not been initialized for the module service.");
329                    }
330                    int classModifiers = externalizableBusinessObjectInterface.getModifiers();
331            Map<Class, Class> ebos = getModuleConfiguration().getExternalizableBusinessObjectImplementations();
332    
333                    if (ebos.containsValue(externalizableBusinessObjectInterface)) {
334                            return externalizableBusinessObjectInterface;
335                    }
336                    if (getModuleConfiguration().getExternalizableBusinessObjectImplementations() == null) {
337                            return null;
338                    }
339                    else {
340                            Class<E> implementationClass = ebos.get(externalizableBusinessObjectInterface);
341                            int implClassModifiers = implementationClass.getModifiers();
342                            if (Modifier.isInterface(implClassModifiers) || Modifier.isAbstract(implClassModifiers)) {
343                                    throw new RuntimeException("Implementation class must be non-abstract class: ebo interface: " + externalizableBusinessObjectInterface.getName() + " impl class: "
344                                                    + implementationClass.getName() + " module: " + getModuleConfiguration().getNamespaceCode());
345                            }
346                            return implementationClass;
347                    }
348    
349            }
350    
351            /***
352             * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
353             */
354            public void afterPropertiesSet() throws Exception {
355                    KualiModuleService kualiModuleService = null;
356                    try {
357                            kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService();
358                            if ( kualiModuleService == null ) {
359                                    kualiModuleService = ((KualiModuleService)applicationContext.getBean( KRADServiceLocatorWeb.KUALI_MODULE_SERVICE ));
360                            }
361                    } catch ( NoSuchBeanDefinitionException ex ) {
362                            kualiModuleService = ((KualiModuleService)applicationContext.getBean( KRADServiceLocatorWeb.KUALI_MODULE_SERVICE ));
363                    }
364                    kualiModuleService.getInstalledModuleServices().add( this );
365            }
366    
367            /**
368             * @return the moduleConfiguration
369             */
370            public ModuleConfiguration getModuleConfiguration() {
371                    return this.moduleConfiguration;
372            }
373    
374            /**
375             * @param moduleConfiguration the moduleConfiguration to set
376             */
377            public void setModuleConfiguration(ModuleConfiguration moduleConfiguration) {
378                    this.moduleConfiguration = moduleConfiguration;
379            }
380    
381        /***
382         * @see org.kuali.rice.krad.service.ModuleService#isExternalizable(java.lang.Class)
383         */
384        public boolean isExternalizable(Class boClazz){
385            if(boClazz==null) return false;
386            return ExternalizableBusinessObject.class.isAssignableFrom(boClazz);
387        }
388    
389            public boolean isExternalizableBusinessObjectLookupable(Class boClass) {
390                    return getBusinessObjectDictionaryService().isLookupable(boClass);
391            }
392    
393            public boolean isExternalizableBusinessObjectInquirable(Class boClass) {
394                    return getBusinessObjectDictionaryService().isInquirable(boClass);
395            }
396    
397            public <T extends ExternalizableBusinessObject> T createNewObjectFromExternalizableClass(Class<T> boClass) {
398                    try {
399                            return (T) getExternalizableBusinessObjectImplementation(boClass).newInstance();
400                    } catch (Exception e) {
401                            throw new RuntimeException("Unable to create externalizable business object class", e);
402                    }
403            }
404    
405            public DataObjectRelationship getBusinessObjectRelationship(Class boClass, String attributeName, String attributePrefix){
406                    return null;
407            }
408    
409    
410    
411            public BusinessObjectDictionaryService getBusinessObjectDictionaryService () {
412                    if ( businessObjectDictionaryService == null ) {
413                            businessObjectDictionaryService = KRADServiceLocatorWeb.getBusinessObjectDictionaryService();
414                    }
415                    return businessObjectDictionaryService;
416            }
417    
418            /**
419             * @return the businessObjectService
420             */
421            public BusinessObjectService getBusinessObjectService() {
422                    if ( businessObjectService == null ) {
423                            businessObjectService = KRADServiceLocator.getBusinessObjectService();
424                    }
425                    return businessObjectService;
426            }
427    
428        /**
429         * Gets the lookupService attribute.
430         * @return Returns the lookupService.
431         */
432        protected LookupService getLookupService() {
433            return lookupService != null ? lookupService : KRADServiceLocatorWeb.getLookupService();
434        }
435    
436            /**
437             * @return the kualiModuleService
438             */
439            public KualiModuleService getKualiModuleService() {
440                    return this.kualiModuleService;
441            }
442    
443            /**
444             * @param kualiModuleService the kualiModuleService to set
445             */
446            public void setKualiModuleService(KualiModuleService kualiModuleService) {
447                    this.kualiModuleService = kualiModuleService;
448            }
449    
450            /**
451             * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
452             */
453            public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
454                    this.applicationContext = applicationContext;
455            }
456    
457    
458    
459            /**
460             * This overridden method ...
461             *
462             * @see org.kuali.rice.krad.service.ModuleService#listAlternatePrimaryKeyFieldNames(java.lang.Class)
463             */
464            public List<List<String>> listAlternatePrimaryKeyFieldNames(
465                            Class businessObjectInterfaceClass) {
466                    return null;
467            }
468    
469    
470    
471        /**
472         * This method determines whether or not this module is currently locked
473         * 
474         * @see org.kuali.rice.krad.service.ModuleService#isLocked()
475         */
476        @Override
477        public boolean isLocked() {
478            ModuleConfiguration configuration = this.getModuleConfiguration();
479            if(configuration != null) {
480                String namespaceCode = configuration.getNamespaceCode();
481                String componentCode = KRADConstants.DetailTypes.OLTP_LOCKOUT_DETAIL_TYPE;
482                String parameterName = KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_ACTIVE_IND;
483                ParameterService parameterService = CoreFrameworkServiceLocator.getParameterService();
484                String shouldLockout = parameterService.getParameterValueAsString(namespaceCode, componentCode, parameterName);
485                if(StringUtils.isNotBlank(shouldLockout)) {
486                    return parameterService.getParameterValueAsBoolean(namespaceCode, componentCode, parameterName);
487                }
488            }
489            return false;
490        }
491    }
492