Coverage Report - org.kuali.rice.krad.inquiry.InquirableImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
InquirableImpl
0%
0/115
0%
0/68
4.154
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.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/ecl2.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
 70  
      */
 71  0
     public static List<Class<?>> SUPER_CLASS_TRANSLATOR_LIST = new ArrayList<Class<?>>();
 72  
 
 73  
     /**
 74  
      * Finds primary and alternate key sets configured for the configured data object class and
 75  
      * then attempts to find a set with matching key/value pairs from the request, if a set is
 76  
      * found then calls the module service (for EBOs) or business object service to retrieve
 77  
      * the data object
 78  
      *
 79  
      * <p>
 80  
      * Note at this point on business objects are supported by the default implementation
 81  
      * </p>
 82  
      *
 83  
      * @see Inquirable#retrieveDataObject(java.util.Map<java.lang.String,java.lang.String>)
 84  
      */
 85  
     @Override
 86  
     public Object retrieveDataObject(Map<String, String> parameters) {
 87  0
         if (dataObjectClass == null) {
 88  0
             LOG.error("Data object class must be set in inquirable before retrieving the object");
 89  0
             throw new RuntimeException("Data object class must be set in inquirable before retrieving the object");
 90  
         }
 91  
 
 92  
         // build list of key values from the map parameters
 93  0
         List<String> pkPropertyNames = getDataObjectMetaDataService().listPrimaryKeyFieldNames(dataObjectClass);
 94  
 
 95  
         // some classes might have alternate keys defined for retrieving
 96  0
         List<List<String>> alternateKeyNames = this.getAlternateKeysForClass(dataObjectClass);
 97  
 
 98  
         // add pk set as beginning so it will be checked first for match
 99  0
         alternateKeyNames.add(0, pkPropertyNames);
 100  
 
 101  0
         List<String> dataObjectKeySet = retrieveKeySetFromMap(alternateKeyNames, parameters);
 102  0
         if ((dataObjectKeySet == null) || dataObjectKeySet.isEmpty()) {
 103  0
             LOG.warn("Matching key set not found in request for class: " + getDataObjectClass());
 104  
 
 105  0
             return null;
 106  
         }
 107  
 
 108  
         // found key set, now build map of key values pairs we can use to retrieve the object
 109  0
         Map<String, Object> keyPropertyValues = new HashMap<String, Object>();
 110  0
         for (String keyPropertyName : dataObjectKeySet) {
 111  0
             String keyPropertyValue = parameters.get(keyPropertyName);
 112  
 
 113  
             // uppercase value if needed
 114  0
             Boolean forceUppercase = Boolean.FALSE;
 115  
             try {
 116  0
                 forceUppercase = getDataDictionaryService().getAttributeForceUppercase(dataObjectClass,
 117  
                         keyPropertyName);
 118  0
             } catch (UnknownBusinessClassAttributeException ex) {
 119  
                 // swallowing exception because this check for ForceUppercase would
 120  
                 // require a DD entry for the attribute, and we will just set force uppercase to false
 121  0
                 LOG.warn("Data object class "
 122  
                         + dataObjectClass
 123  
                         + " property "
 124  
                         + keyPropertyName
 125  
                         + " should probably have a DD definition.", ex);
 126  0
             }
 127  
 
 128  0
             if (forceUppercase.booleanValue() && (keyPropertyValue != null)) {
 129  0
                 keyPropertyValue = keyPropertyValue.toUpperCase();
 130  
             }
 131  
 
 132  
             // check security on key field
 133  0
             if (getDataObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObjectClass,
 134  
                     keyPropertyName)) {
 135  
                 try {
 136  0
                     keyPropertyValue = getEncryptionService().decrypt(keyPropertyValue);
 137  0
                 } catch (GeneralSecurityException e) {
 138  0
                     LOG.error("Data object class "
 139  
                             + dataObjectClass
 140  
                             + " property "
 141  
                             + keyPropertyName
 142  
                             + " should have been encrypted, but there was a problem decrypting it.", e);
 143  0
                     throw new RuntimeException("Data object class "
 144  
                             + dataObjectClass
 145  
                             + " property "
 146  
                             + keyPropertyName
 147  
                             + " should have been encrypted, but there was a problem decrypting it.", e);
 148  0
                 }
 149  
             }
 150  
 
 151  0
             keyPropertyValues.put(keyPropertyName, keyPropertyValue);
 152  0
         }
 153  
 
 154  
         // now retrieve the object based on the key set
 155  0
         Object dataObject = null;
 156  
 
 157  0
         ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(
 158  
                 getDataObjectClass());
 159  0
         if (moduleService != null && moduleService.isExternalizable(getDataObjectClass())) {
 160  0
             dataObject = moduleService.getExternalizableBusinessObject(getDataObjectClass().asSubclass(
 161  
                     ExternalizableBusinessObject.class), keyPropertyValues);
 162  0
         } else if (BusinessObject.class.isAssignableFrom(getDataObjectClass())) {
 163  0
             dataObject = getBusinessObjectService().findByPrimaryKey(getDataObjectClass().asSubclass(
 164  
                     BusinessObject.class), keyPropertyValues);
 165  
         }
 166  
 
 167  0
         return dataObject;
 168  
     }
 169  
 
 170  
     /**
 171  
      * Iterates through the list of key sets looking for a set where the given map of parameters has
 172  
      * all the key names and values are non-blank, first matched set is returned
 173  
      *
 174  
      * @param potentialKeySets - List of key sets to check for match
 175  
      * @param parameters - map of parameter name/value pairs for matching key set
 176  
      * @return List<String> key set that was matched, or null if none were matched
 177  
      */
 178  
     protected List<String> retrieveKeySetFromMap(List<List<String>> potentialKeySets, Map<String, String> parameters) {
 179  0
         List<String> foundKeySet = null;
 180  
 
 181  0
         for (List<String> potentialKeySet : potentialKeySets) {
 182  0
             boolean keySetMatch = true;
 183  0
             for (String keyName : potentialKeySet) {
 184  0
                 if (!parameters.containsKey(keyName) || StringUtils.isBlank(parameters.get(keyName))) {
 185  0
                     keySetMatch = false;
 186  
                 }
 187  
             }
 188  
 
 189  0
             if (keySetMatch) {
 190  0
                 foundKeySet = potentialKeySet;
 191  0
                 break;
 192  
             }
 193  0
         }
 194  
 
 195  0
         return foundKeySet;
 196  
     }
 197  
 
 198  
     /**
 199  
      * Invokes the module service to retrieve any alternate keys that have been
 200  
      * defined for the given class
 201  
      *
 202  
      * @param clazz - class to find alternate keys for
 203  
      * @return List<List<String>> list of alternate key sets, or empty list if none are found
 204  
      */
 205  
     protected List<List<String>> getAlternateKeysForClass(Class<?> clazz) {
 206  0
         KualiModuleService kualiModuleService = getKualiModuleService();
 207  0
         ModuleService moduleService = kualiModuleService.getResponsibleModuleService(clazz);
 208  
 
 209  0
         List<List<String>> altKeys = null;
 210  0
         if (moduleService != null) {
 211  0
             altKeys = moduleService.listAlternatePrimaryKeyFieldNames(clazz);
 212  
         }
 213  
 
 214  0
         return altKeys != null ? altKeys : new ArrayList<List<String>>();
 215  
     }
 216  
 
 217  
     /**
 218  
      * @see Inquirable#buildInquirableLink(java.lang.Object,
 219  
      *      java.lang.String, org.kuali.rice.krad.uif.widget.Inquiry)
 220  
      */
 221  
     @Override
 222  
     public void buildInquirableLink(Object dataObject, String propertyName, Inquiry inquiry) {
 223  0
         Class<?> inquiryObjectClass = null;
 224  
 
 225  
         // inquiry into data object class if property is title attribute
 226  0
         Class<?> objectClass = ObjectUtils.materializeClassForProxiedObject(dataObject);
 227  0
         if (propertyName.equals(getDataObjectMetaDataService().getTitleAttribute(objectClass))) {
 228  0
             inquiryObjectClass = objectClass;
 229  0
         } else if (ObjectUtils.isNestedAttribute(propertyName)) {
 230  0
             String nestedPropertyName = ObjectUtils.getNestedAttributePrefix(propertyName);
 231  0
             Object nestedPropertyObject = ObjectUtils.getNestedValue(dataObject, nestedPropertyName);
 232  
 
 233  0
             if (ObjectUtils.isNotNull(nestedPropertyObject)) {
 234  0
                 String nestedPropertyPrimitive = ObjectUtils.getNestedAttributePrimitive(propertyName);
 235  0
                 Class<?> nestedPropertyObjectClass = ObjectUtils.materializeClassForProxiedObject(nestedPropertyObject);
 236  
 
 237  0
                 if (nestedPropertyPrimitive.equals(getDataObjectMetaDataService().getTitleAttribute(
 238  
                         nestedPropertyObjectClass))) {
 239  0
                     inquiryObjectClass = nestedPropertyObjectClass;
 240  
                 }
 241  
             }
 242  
         }
 243  
 
 244  
         // if not title, then get primary relationship
 245  0
         DataObjectRelationship relationship = null;
 246  0
         if (inquiryObjectClass == null) {
 247  0
             relationship = getDataObjectMetaDataService().getDataObjectRelationship(dataObject, objectClass,
 248  
                     propertyName, "", true, false, true);
 249  0
             if (relationship != null) {
 250  0
                 inquiryObjectClass = relationship.getRelatedClass();
 251  
             }
 252  
         }
 253  
 
 254  
         // if haven't found inquiry class, then no inquiry can be rendered
 255  0
         if (inquiryObjectClass == null) {
 256  0
             inquiry.setRender(false);
 257  
 
 258  0
             return;
 259  
         }
 260  
 
 261  0
         if (DocumentHeader.class.isAssignableFrom(inquiryObjectClass)) {
 262  0
             String documentNumber = (String) ObjectUtils.getPropertyValue(dataObject, propertyName);
 263  0
             if (StringUtils.isNotBlank(documentNumber)) {
 264  0
                 inquiry.getInquiryLinkField().setHrefText(getConfigurationService().getPropertyValueAsString(
 265  
                         KRADConstants.WORKFLOW_URL_KEY)
 266  
                         + KRADConstants.DOCHANDLER_DO_URL
 267  
                         + documentNumber
 268  
                         + KRADConstants.DOCHANDLER_URL_CHUNK);
 269  0
                 inquiry.getInquiryLinkField().setLinkLabel(documentNumber);
 270  0
                 inquiry.setRender(true);
 271  
             }
 272  
 
 273  0
             return;
 274  
         }
 275  
 
 276  0
         synchronized (SUPER_CLASS_TRANSLATOR_LIST) {
 277  0
             for (Class<?> clazz : SUPER_CLASS_TRANSLATOR_LIST) {
 278  0
                 if (clazz.isAssignableFrom(inquiryObjectClass)) {
 279  0
                     inquiryObjectClass = clazz;
 280  0
                     break;
 281  
                 }
 282  
             }
 283  0
         }
 284  
 
 285  0
         if (!inquiryObjectClass.isInterface() && ExternalizableBusinessObject.class.isAssignableFrom(
 286  
                 inquiryObjectClass)) {
 287  0
             inquiryObjectClass = ExternalizableBusinessObjectUtils.determineExternalizableBusinessObjectSubInterface(
 288  
                     inquiryObjectClass);
 289  
         }
 290  
 
 291  
         // listPrimaryKeyFieldNames returns an unmodifiable list. So a copy is necessary.
 292  0
         List<String> keys = new ArrayList<String>(getDataObjectMetaDataService().listPrimaryKeyFieldNames(
 293  
                 inquiryObjectClass));
 294  
 
 295  0
         if (keys == null) {
 296  0
             keys = Collections.emptyList();
 297  
         }
 298  
 
 299  
         // build inquiry parameter mappings
 300  0
         Map<String, String> inquiryParameters = new HashMap<String, String>();
 301  0
         for (String keyName : keys) {
 302  0
             String keyConversion = keyName;
 303  0
             if (relationship != null) {
 304  0
                 keyConversion = relationship.getParentAttributeForChildAttribute(keyName);
 305  0
             } else if (ObjectUtils.isNestedAttribute(propertyName)) {
 306  0
                 String nestedAttributePrefix = ObjectUtils.getNestedAttributePrefix(propertyName);
 307  0
                 keyConversion = nestedAttributePrefix + "." + keyName;
 308  
             }
 309  
 
 310  0
             inquiryParameters.put(keyConversion, keyName);
 311  0
         }
 312  
 
 313  0
         inquiry.buildInquiryLink(dataObject, propertyName, inquiryObjectClass, inquiryParameters);
 314  0
     }
 315  
 
 316  
     /**
 317  
      * @see Inquirable#setDataObjectClass(java.lang.Class)
 318  
      */
 319  
     @Override
 320  
     public void setDataObjectClass(Class<?> dataObjectClass) {
 321  0
         this.dataObjectClass = dataObjectClass;
 322  0
     }
 323  
 
 324  
     /**
 325  
      * Retrieves the data object class configured for this inquirable
 326  
      *
 327  
      * @return Class<?> of configured data object, or null if data object class not configured
 328  
      */
 329  
     protected Class<?> getDataObjectClass() {
 330  0
         return this.dataObjectClass;
 331  
     }
 332  
 
 333  
     protected ConfigurationService getConfigurationService() {
 334  0
         return KRADServiceLocator.getKualiConfigurationService();
 335  
     }
 336  
 
 337  
     protected DataObjectMetaDataService getDataObjectMetaDataService() {
 338  0
         return KRADServiceLocatorWeb.getDataObjectMetaDataService();
 339  
     }
 340  
 
 341  
     protected KualiModuleService getKualiModuleService() {
 342  0
         return KRADServiceLocatorWeb.getKualiModuleService();
 343  
     }
 344  
 
 345  
     protected DataDictionaryService getDataDictionaryService() {
 346  0
         return KRADServiceLocatorWeb.getDataDictionaryService();
 347  
     }
 348  
 
 349  
     protected DataObjectAuthorizationService getDataObjectAuthorizationService() {
 350  0
         return KRADServiceLocatorWeb.getDataObjectAuthorizationService();
 351  
     }
 352  
 
 353  
     protected EncryptionService getEncryptionService() {
 354  0
         return CoreApiServiceLocator.getEncryptionService();
 355  
     }
 356  
 
 357  
     protected BusinessObjectService getBusinessObjectService() {
 358  0
         return KRADServiceLocator.getBusinessObjectService();
 359  
     }
 360  
 
 361  
 }