Coverage Report - org.kuali.rice.krad.inquiry.InquirableImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
InquirableImpl
0%
0/143
0%
0/80
3.35
 
 1  
 /*
 2  
  * Copyright 2007 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 1.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl1.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.krad.inquiry;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.kuali.rice.core.api.CoreApiServiceLocator;
 20  
 import org.kuali.rice.core.api.config.property.ConfigurationService;
 21  
 import org.kuali.rice.core.api.encryption.EncryptionService;
 22  
 import org.kuali.rice.krad.bo.BusinessObject;
 23  
 import org.kuali.rice.krad.bo.DataObjectRelationship;
 24  
 import org.kuali.rice.krad.bo.DocumentHeader;
 25  
 import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
 26  
 import org.kuali.rice.krad.datadictionary.exception.UnknownBusinessClassAttributeException;
 27  
 import org.kuali.rice.krad.service.BusinessObjectService;
 28  
 import org.kuali.rice.krad.service.DataDictionaryService;
 29  
 import org.kuali.rice.krad.service.DataObjectAuthorizationService;
 30  
 import org.kuali.rice.krad.service.DataObjectMetaDataService;
 31  
 import org.kuali.rice.krad.service.KRADServiceLocator;
 32  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 33  
 import org.kuali.rice.krad.service.KualiModuleService;
 34  
 import org.kuali.rice.krad.service.ModuleService;
 35  
 import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl;
 36  
 import org.kuali.rice.krad.uif.widget.Inquiry;
 37  
 import org.kuali.rice.krad.util.ExternalizableBusinessObjectUtils;
 38  
 import org.kuali.rice.krad.util.KRADConstants;
 39  
 import org.kuali.rice.krad.util.ObjectUtils;
 40  
 
 41  
 import java.security.GeneralSecurityException;
 42  
 import java.util.ArrayList;
 43  
 import java.util.Collections;
 44  
 import java.util.HashMap;
 45  
 import java.util.List;
 46  
 import java.util.Map;
 47  
 
 48  
 /**
 49  
  * Implementation of the <code>Inquirable</code> interface that uses metadata
 50  
  * from the data dictionary and performs a query against the database to retrieve
 51  
  * the data object for inquiry
 52  
  *
 53  
  * <p>
 54  
  * More advanced lookup operations or alternate ways of retrieving metadata can
 55  
  * be implemented by extending this base implementation and configuring
 56  
  * </p>
 57  
  *
 58  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 59  
  */
 60  0
 public class InquirableImpl extends ViewHelperServiceImpl implements Inquirable {
 61  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(InquirableImpl.class);
 62  
 
 63  
     protected Class<?> dataObjectClass;
 64  
 
 65  
     /**
 66  
      * A list that can be used to define classes that are superclasses or
 67  
      * superinterfaces of kuali objects where those objects' inquiry URLs need
 68  
      * to use the name of the superclass or superinterface as the business
 69  
      * object class attribute (see
 70  
      * {@link RiceConstants#BUSINESS_OBJECT_CLASS_ATTRIBUTE)
 71  
      */
 72  0
     public static List<Class<?>> SUPER_CLASS_TRANSLATOR_LIST = new ArrayList<Class<?>>();
 73  
 
 74  
     private ConfigurationService configurationService;
 75  
     private DataObjectMetaDataService dataObjectMetaDataService;
 76  
     private KualiModuleService kualiModuleService;
 77  
     private DataDictionaryService dataDictionaryService;
 78  
     private DataObjectAuthorizationService dataObjectAuthorizationService;
 79  
     private EncryptionService encryptionService;
 80  
     private BusinessObjectService businessObjectService;
 81  
 
 82  
     /**
 83  
      * Finds primary and alternate key sets configured for the configured data object class and
 84  
      * then attempts to find a set with matching key/value pairs from the request, if a set is
 85  
      * found then calls the module service (for EBOs) or business object service to retrieve
 86  
      * the data object
 87  
      *
 88  
      * <p>
 89  
      * Note at this point on business objects are supported by the default implementation
 90  
      * </p>
 91  
      *
 92  
      * @see Inquirable#retrieveDataObject(java.util.Map<java.lang.String,java.lang.String>)
 93  
      */
 94  
     @Override
 95  
     public Object retrieveDataObject(Map<String, String> parameters) {
 96  0
         if (dataObjectClass == null) {
 97  0
             LOG.error("Data object class must be set in inquirable before retrieving the object");
 98  0
             throw new RuntimeException("Data object class must be set in inquirable before retrieving the object");
 99  
         }
 100  
 
 101  
         // build list of key values from the map parameters
 102  0
         List<String> pkPropertyNames = getDataObjectMetaDataService().listPrimaryKeyFieldNames(dataObjectClass);
 103  
 
 104  
         // some classes might have alternate keys defined for retrieving
 105  0
         List<List<String>> alternateKeyNames = this.getAlternateKeysForClass(dataObjectClass);
 106  
 
 107  
         // add pk set as beginning so it will be checked first for match
 108  0
         alternateKeyNames.add(0, pkPropertyNames);
 109  
 
 110  0
         List<String> dataObjectKeySet = retrieveKeySetFromMap(alternateKeyNames, parameters);
 111  0
         if ((dataObjectKeySet == null) || dataObjectKeySet.isEmpty()) {
 112  0
             LOG.warn("Matching key set not found in request for class: " + getDataObjectClass());
 113  
 
 114  0
             return null;
 115  
         }
 116  
 
 117  
         // found key set, now build map of key values pairs we can use to retrieve the object
 118  0
         Map<String, Object> keyPropertyValues = new HashMap<String, Object>();
 119  0
         for (String keyPropertyName : dataObjectKeySet) {
 120  0
             String keyPropertyValue = parameters.get(keyPropertyName);
 121  
 
 122  
             // uppercase value if needed
 123  0
             Boolean forceUppercase = Boolean.FALSE;
 124  
             try {
 125  0
                 forceUppercase = dataDictionaryService.getAttributeForceUppercase(dataObjectClass, keyPropertyName);
 126  0
             } catch (UnknownBusinessClassAttributeException ex) {
 127  
                 // swallowing exception because this check for ForceUppercase would
 128  
                 // require a DD entry for the attribute, and we will just set force uppercase to false
 129  0
                 LOG.warn("Data object class " + dataObjectClass + " property " + keyPropertyName +
 130  
                         " should probably have a DD definition.", ex);
 131  0
             }
 132  
 
 133  0
             if (forceUppercase.booleanValue()) {
 134  0
                 keyPropertyName = keyPropertyName.toUpperCase();
 135  
             }
 136  
 
 137  
             // check security on key field
 138  0
             if (getDataObjectAuthorizationService()
 139  
                     .attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObjectClass, keyPropertyName)) {
 140  
                 try {
 141  0
                     keyPropertyValue = encryptionService.decrypt(keyPropertyValue);
 142  0
                 } catch (GeneralSecurityException e) {
 143  0
                     LOG.error("Data object class " + dataObjectClass + " property " + keyPropertyName +
 144  
                             " should have been encrypted, but there was a problem decrypting it.", e);
 145  0
                     throw new RuntimeException("Data object class " + dataObjectClass + " property " + keyPropertyName +
 146  
                             " should have been encrypted, but there was a problem decrypting it.", e);
 147  0
                 }
 148  
             }
 149  
 
 150  0
             keyPropertyValues.put(keyPropertyName, keyPropertyValue);
 151  0
         }
 152  
 
 153  
         // now retrieve the object based on the key set
 154  0
         Object dataObject = null;
 155  
 
 156  0
         ModuleService moduleService =
 157  
                 KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(getDataObjectClass());
 158  0
         if (moduleService != null && moduleService.isExternalizable(getDataObjectClass())) {
 159  0
             dataObject = moduleService.getExternalizableBusinessObject(
 160  
                     getDataObjectClass().asSubclass(ExternalizableBusinessObject.class), keyPropertyValues);
 161  0
         } else if (BusinessObject.class.isAssignableFrom(getDataObjectClass())) {
 162  0
             dataObject = getBusinessObjectService()
 163  
                     .findByPrimaryKey(getDataObjectClass().asSubclass(BusinessObject.class), keyPropertyValues);
 164  
         }
 165  
 
 166  0
         return dataObject;
 167  
     }
 168  
 
 169  
     /**
 170  
      * Iterates through the list of key sets looking for a set where the given map of parameters has
 171  
      * all the key names and values are non-blank, first matched set is returned
 172  
      *
 173  
      * @param potentialKeySets - List of key sets to check for match
 174  
      * @param parameters - map of parameter name/value pairs for matching key set
 175  
      * @return List<String> key set that was matched, or null if none were matched
 176  
      */
 177  
     protected List<String> retrieveKeySetFromMap(List<List<String>> potentialKeySets, Map<String, String> parameters) {
 178  0
         List<String> foundKeySet = null;
 179  
 
 180  0
         for (List<String> potentialKeySet : potentialKeySets) {
 181  0
             boolean keySetMatch = true;
 182  0
             for (String keyName : potentialKeySet) {
 183  0
                 if (!parameters.containsKey(keyName) || StringUtils.isBlank(parameters.get(keyName))) {
 184  0
                     keySetMatch = false;
 185  
                 }
 186  
             }
 187  
 
 188  0
             if (keySetMatch) {
 189  0
                 foundKeySet = potentialKeySet;
 190  0
                 break;
 191  
             }
 192  0
         }
 193  
 
 194  0
         return foundKeySet;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Invokes the module service to retrieve any alternate keys that have been
 199  
      * defined for the given class
 200  
      *
 201  
      * @param clazz - class to find alternate keys for
 202  
      * @return List<List<String>> list of alternate key sets, or empty list if none are found
 203  
      */
 204  
     protected List<List<String>> getAlternateKeysForClass(Class<?> clazz) {
 205  0
         KualiModuleService kualiModuleService = getKualiModuleService();
 206  0
         ModuleService moduleService = kualiModuleService.getResponsibleModuleService(clazz);
 207  
 
 208  0
         List<List<String>> altKeys = null;
 209  0
         if (moduleService != null) {
 210  0
             altKeys = moduleService.listAlternatePrimaryKeyFieldNames(clazz);
 211  
         }
 212  
 
 213  0
         return altKeys != null ? altKeys : new ArrayList<List<String>>();
 214  
     }
 215  
 
 216  
     /**
 217  
      * @see Inquirable#buildInquirableLink(java.lang.Object,
 218  
      *      java.lang.String, org.kuali.rice.krad.uif.widget.Inquiry)
 219  
      */
 220  
     @Override
 221  
     public void buildInquirableLink(Object dataObject, String propertyName, Inquiry inquiry) {
 222  0
         Class<?> inquiryObjectClass = null;
 223  
 
 224  
         // inquiry into data object class if property is title attribute
 225  0
         Class<?> objectClass = ObjectUtils.materializeClassForProxiedObject(dataObject);
 226  0
         if (propertyName.equals(getDataObjectMetaDataService().getTitleAttribute(objectClass))) {
 227  0
             inquiryObjectClass = objectClass;
 228  0
         } else if (ObjectUtils.isNestedAttribute(propertyName)) {
 229  0
             String nestedPropertyName = ObjectUtils.getNestedAttributePrefix(propertyName);
 230  0
             Object nestedPropertyObject = ObjectUtils.getNestedValue(dataObject, nestedPropertyName);
 231  
 
 232  0
             if (ObjectUtils.isNotNull(nestedPropertyObject)) {
 233  0
                 String nestedPropertyPrimitive = ObjectUtils.getNestedAttributePrimitive(propertyName);
 234  0
                 Class<?> nestedPropertyObjectClass = ObjectUtils.materializeClassForProxiedObject(nestedPropertyObject);
 235  
 
 236  0
                 if (nestedPropertyPrimitive
 237  
                         .equals(getDataObjectMetaDataService().getTitleAttribute(nestedPropertyObjectClass))) {
 238  0
                     inquiryObjectClass = nestedPropertyObjectClass;
 239  
                 }
 240  
             }
 241  
         }
 242  
 
 243  
         // if not title, then get primary relationship
 244  0
         DataObjectRelationship relationship = null;
 245  0
         if (inquiryObjectClass == null) {
 246  0
             relationship = getDataObjectMetaDataService()
 247  
                     .getDataObjectRelationship(dataObject, objectClass, propertyName, "", true, false, true);
 248  0
             if (relationship != null) {
 249  0
                 inquiryObjectClass = relationship.getRelatedClass();
 250  
             }
 251  
         }
 252  
 
 253  
         // if haven't found inquiry class, then no inquiry can be rendered
 254  0
         if (inquiryObjectClass == null) {
 255  0
             inquiry.setRender(false);
 256  
 
 257  0
             return;
 258  
         }
 259  
 
 260  0
         if (DocumentHeader.class.isAssignableFrom(inquiryObjectClass)) {
 261  0
             String documentNumber = (String) ObjectUtils.getPropertyValue(dataObject, propertyName);
 262  0
             if (StringUtils.isNotBlank(documentNumber)) {
 263  0
                 inquiry.getInquiryLinkField().setHrefText(
 264  
                         getConfigurationService().getPropertyValueAsString(KRADConstants.WORKFLOW_URL_KEY) +
 265  
                                 KRADConstants.DOCHANDLER_DO_URL + documentNumber + KRADConstants.DOCHANDLER_URL_CHUNK);
 266  0
                 inquiry.getInquiryLinkField().setLinkLabel(documentNumber);
 267  0
                 inquiry.setRender(true);
 268  
             }
 269  
 
 270  0
             return;
 271  
         }
 272  
 
 273  0
         synchronized (SUPER_CLASS_TRANSLATOR_LIST) {
 274  0
             for (Class<?> clazz : SUPER_CLASS_TRANSLATOR_LIST) {
 275  0
                 if (clazz.isAssignableFrom(inquiryObjectClass)) {
 276  0
                     inquiryObjectClass = clazz;
 277  0
                     break;
 278  
                 }
 279  
             }
 280  0
         }
 281  
 
 282  0
         if (!inquiryObjectClass.isInterface() &&
 283  
                 ExternalizableBusinessObject.class.isAssignableFrom(inquiryObjectClass)) {
 284  0
             inquiryObjectClass = ExternalizableBusinessObjectUtils
 285  
                     .determineExternalizableBusinessObjectSubInterface(inquiryObjectClass);
 286  
         }
 287  
 
 288  
         // listPrimaryKeyFieldNames returns an unmodifiable list. So a copy is
 289  
         // necessary.
 290  0
         List<String> keys =
 291  
                 new ArrayList<String>(getDataObjectMetaDataService().listPrimaryKeyFieldNames(inquiryObjectClass));
 292  
 
 293  0
         if (keys == null) {
 294  0
             keys = Collections.emptyList();
 295  
         }
 296  
 
 297  
         // build inquiry parameter mappings
 298  0
         Map<String, String> inquiryParameters = new HashMap<String, String>();
 299  0
         for (String keyName : keys) {
 300  0
             String keyConversion = keyName;
 301  0
             if (relationship != null) {
 302  0
                 keyConversion = relationship.getChildAttributeForParentAttribute(keyName);
 303  0
             } else if (ObjectUtils.isNestedAttribute(propertyName)) {
 304  0
                 String nestedAttributePrefix = ObjectUtils.getNestedAttributePrefix(propertyName);
 305  0
                 keyConversion = nestedAttributePrefix + "." + keyName;
 306  
             }
 307  
 
 308  0
             inquiryParameters.put(keyConversion, keyName);
 309  0
         }
 310  
 
 311  0
         inquiry.buildInquiryLink(dataObject, propertyName, inquiryObjectClass, inquiryParameters);
 312  0
     }
 313  
 
 314  
     /**
 315  
      * @see Inquirable#setDataObjectClass(java.lang.Class)
 316  
      */
 317  
     @Override
 318  
     public void setDataObjectClass(Class<?> dataObjectClass) {
 319  0
         this.dataObjectClass = dataObjectClass;
 320  0
     }
 321  
 
 322  
     /**
 323  
      * Retrieves the data object class configured for this inquirable
 324  
      *
 325  
      * @return Class<?> of configured data object, or null if data object class not configured
 326  
      */
 327  
     protected Class<?> getDataObjectClass() {
 328  0
         return this.dataObjectClass;
 329  
     }
 330  
 
 331  
     protected ConfigurationService getConfigurationService() {
 332  0
         if (configurationService == null) {
 333  0
             configurationService = KRADServiceLocator.getKualiConfigurationService();
 334  
         }
 335  0
         return this.configurationService;
 336  
     }
 337  
 
 338  
     public void setConfigurationService(ConfigurationService configurationService) {
 339  0
         this.configurationService = configurationService;
 340  0
     }
 341  
 
 342  
     protected DataObjectMetaDataService getDataObjectMetaDataService() {
 343  0
         if (dataObjectMetaDataService == null) {
 344  0
             this.dataObjectMetaDataService = KRADServiceLocatorWeb.getDataObjectMetaDataService();
 345  
         }
 346  0
         return dataObjectMetaDataService;
 347  
     }
 348  
 
 349  
     public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) {
 350  0
         this.dataObjectMetaDataService = dataObjectMetaDataService;
 351  0
     }
 352  
 
 353  
     protected KualiModuleService getKualiModuleService() {
 354  0
         if (kualiModuleService == null) {
 355  0
             this.kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService();
 356  
         }
 357  0
         return kualiModuleService;
 358  
     }
 359  
 
 360  
     public void setKualiModuleService(KualiModuleService kualiModuleService) {
 361  0
         this.kualiModuleService = kualiModuleService;
 362  0
     }
 363  
 
 364  
     protected DataDictionaryService getDataDictionaryService() {
 365  0
         if (dataDictionaryService == null) {
 366  0
             this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
 367  
         }
 368  0
         return dataDictionaryService;
 369  
     }
 370  
 
 371  
     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
 372  0
         this.dataDictionaryService = dataDictionaryService;
 373  0
     }
 374  
 
 375  
     protected DataObjectAuthorizationService getDataObjectAuthorizationService() {
 376  0
         if (dataObjectAuthorizationService == null) {
 377  0
             this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService();
 378  
         }
 379  0
         return dataObjectAuthorizationService;
 380  
     }
 381  
 
 382  
     public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) {
 383  0
         this.dataObjectAuthorizationService = dataObjectAuthorizationService;
 384  0
     }
 385  
 
 386  
     protected EncryptionService getEncryptionService() {
 387  0
         if (encryptionService == null) {
 388  0
             this.encryptionService = CoreApiServiceLocator.getEncryptionService();
 389  
         }
 390  0
         return encryptionService;
 391  
     }
 392  
 
 393  
     public void setEncryptionService(EncryptionService encryptionService) {
 394  0
         this.encryptionService = encryptionService;
 395  0
     }
 396  
 
 397  
     protected BusinessObjectService getBusinessObjectService() {
 398  0
         if (businessObjectService == null) {
 399  0
             this.businessObjectService = KRADServiceLocator.getBusinessObjectService();
 400  
         }
 401  0
         return businessObjectService;
 402  
     }
 403  
 
 404  
     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
 405  0
         this.businessObjectService = businessObjectService;
 406  0
     }
 407  
 }