Coverage Report - org.kuali.rice.kim.service.impl.PersonServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PersonServiceImpl
0%
0/419
0%
0/238
4.816
 
 1  
 /*
 2  
  * Copyright 2007 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.kim.service.impl;
 17  
 
 18  
 import com.google.common.collect.MapMaker;
 19  
 import org.apache.commons.beanutils.PropertyUtils;
 20  
 import org.apache.commons.lang.StringUtils;
 21  
 import org.apache.log4j.Logger;
 22  
 import org.kuali.rice.core.api.CoreApiServiceLocator;
 23  
 import org.kuali.rice.core.api.criteria.Predicate;
 24  
 import org.kuali.rice.core.api.criteria.QueryByCriteria;
 25  
 import org.kuali.rice.kim.api.identity.entity.EntityDefault;
 26  
 import org.kuali.rice.kim.api.identity.entity.EntityDefaultQueryResults;
 27  
 import org.kuali.rice.kim.api.identity.external.EntityExternalIdentifierType;
 28  
 import org.kuali.rice.kim.api.identity.principal.Principal;
 29  
 import org.kuali.rice.kim.api.identity.type.EntityTypeDataDefault;
 30  
 import org.kuali.rice.kim.api.role.RoleService;
 31  
 import org.kuali.rice.kim.api.services.IdentityService;
 32  
 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
 33  
 import org.kuali.rice.kim.bo.Person;
 34  
 import org.kuali.rice.kim.bo.impl.PersonImpl;
 35  
 import org.kuali.rice.kim.service.PersonService;
 36  
 import org.kuali.rice.kim.util.KIMPropertyConstants;
 37  
 import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
 38  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 39  
 import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
 40  
 import org.kuali.rice.krad.bo.BusinessObject;
 41  
 import org.kuali.rice.krad.bo.BusinessObjectRelationship;
 42  
 import org.kuali.rice.krad.lookup.CollectionIncomplete;
 43  
 import org.kuali.rice.krad.util.KRADConstants;
 44  
 import org.kuali.rice.krad.util.KRADPropertyConstants;
 45  
 import org.kuali.rice.krad.util.ObjectUtils;
 46  
 import org.springframework.beans.factory.InitializingBean;
 47  
 
 48  
 import java.security.GeneralSecurityException;
 49  
 import java.util.ArrayList;
 50  
 import java.util.Collection;
 51  
 import java.util.Collections;
 52  
 import java.util.HashMap;
 53  
 import java.util.Iterator;
 54  
 import java.util.List;
 55  
 import java.util.Map;
 56  
 import java.util.concurrent.ConcurrentMap;
 57  
 import java.util.concurrent.TimeUnit;
 58  
 
 59  
 import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
 60  
 
 61  
 /**
 62  
  * This is a description of what this class does - kellerj don't forget to fill this in. 
 63  
  * 
 64  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 65  
  *
 66  
  */
 67  0
 public class PersonServiceImpl implements PersonService, InitializingBean {
 68  
 
 69  0
         private static Logger LOG = Logger.getLogger( PersonServiceImpl.class );
 70  
         protected static final String ENTITY_EXT_ID_PROPERTY_PREFIX = "externalIdentifiers.";
 71  
         protected static final String ENTITY_AFFILIATION_PROPERTY_PREFIX = "affiliations.";
 72  
         protected static final String ENTITY_TYPE_PROPERTY_PREFIX = "entityTypes.";
 73  
         protected static final String ENTITY_EMAIL_PROPERTY_PREFIX = "entityTypes.emailAddresses.";
 74  
         protected static final String ENTITY_PHONE_PROPERTY_PREFIX = "entityTypes.phoneNumbers.";
 75  
         protected static final String ENTITY_ADDRESS_PROPERTY_PREFIX = "entityTypes.addresses.";
 76  
         protected static final String ENTITY_NAME_PROPERTY_PREFIX = "names.";
 77  
         protected static final String PRINCIPAL_PROPERTY_PREFIX = "principals.";
 78  
         protected static final String ENTITY_EMPLOYEE_ID_PROPERTY_PREFIX = "employmentInformation.";
 79  
         // KULRICE-4442 Special handling for extension objects
 80  
         protected static final String EXTENSION = "extension";
 81  
         
 82  
         private IdentityService identityService;
 83  
         private RoleService roleService;
 84  
         private BusinessObjectMetaDataService businessObjectMetaDataService;
 85  
         private MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
 86  
 
 87  
         // Max age defined in seconds
 88  0
         protected int personCacheMaxSize = 3000;
 89  0
         protected int personCacheMaxAgeSeconds = 3600;
 90  
 
 91  
         protected ConcurrentMap<String,Person> personCache;
 92  
         // PERSON/ENTITY RELATED METHODS
 93  
 
 94  0
         protected List<String> personEntityTypeCodes = new ArrayList<String>( 4 );
 95  
         // String that can be passed to the lookup framework to create an type = X OR type = Y criteria
 96  0
         private String personEntityTypeLookupCriteria = null;
 97  
     
 98  0
         protected Map<String,String> baseLookupCriteria = new HashMap<String,String>();
 99  0
         protected Map<String,String> criteriaConversion = new HashMap<String,String>();
 100  0
         protected ArrayList<String> personCachePropertyNames = new ArrayList<String>();
 101  
         {
 102  
                 // init the criteria which will need to be applied to every lookup against
 103  
                 // the identity data tables
 104  0
                 baseLookupCriteria.put( KIMPropertyConstants.Person.ACTIVE, "Y" );
 105  0
                 baseLookupCriteria.put( ENTITY_TYPE_PROPERTY_PREFIX + KRADPropertyConstants.ACTIVE, "Y" );
 106  
                 
 107  
                 // create the field mappings between the Person object and the KimEntity object
 108  0
                 criteriaConversion.put( KIMPropertyConstants.Person.ENTITY_ID, KIMPropertyConstants.Entity.ID);
 109  0
                 criteriaConversion.put( KIMPropertyConstants.Person.ACTIVE, PRINCIPAL_PROPERTY_PREFIX + KRADPropertyConstants.ACTIVE );
 110  0
                 criteriaConversion.put( KIMPropertyConstants.Person.PRINCIPAL_ID, PRINCIPAL_PROPERTY_PREFIX + KIMPropertyConstants.Person.PRINCIPAL_ID );
 111  0
                 criteriaConversion.put( KIMPropertyConstants.Person.PRINCIPAL_NAME, PRINCIPAL_PROPERTY_PREFIX + KIMPropertyConstants.Person.PRINCIPAL_NAME );
 112  0
                 criteriaConversion.put( KIMPropertyConstants.Person.FIRST_NAME, "names.firstName" );
 113  0
                 criteriaConversion.put( KIMPropertyConstants.Person.LAST_NAME, "names.lastName" );
 114  0
                 criteriaConversion.put( KIMPropertyConstants.Person.MIDDLE_NAME, "names.middleName" );
 115  0
                 criteriaConversion.put( KIMPropertyConstants.Person.EMAIL_ADDRESS, "entityTypes.emailAddresses.emailAddress" );
 116  0
                 criteriaConversion.put( KIMPropertyConstants.Person.PHONE_NUMBER, "entityTypes.phoneNumbers.phoneNumber" );
 117  0
                 criteriaConversion.put( KIMPropertyConstants.Person.ADDRESS_LINE_1, "entityTypes.addresses.line1" );
 118  0
                 criteriaConversion.put( KIMPropertyConstants.Person.ADDRESS_LINE_2, "entityTypes.addresses.line2" );
 119  0
                 criteriaConversion.put( KIMPropertyConstants.Person.ADDRESS_LINE_3, "entityTypes.addresses.line3" );
 120  0
                 criteriaConversion.put( KIMPropertyConstants.Person.CITY_NAME, "entityTypes.addresses.cityName" );
 121  0
                 criteriaConversion.put( KIMPropertyConstants.Person.STATE_CODE, "entityTypes.addresses.stateCode" );
 122  0
                 criteriaConversion.put( KIMPropertyConstants.Person.POSTAL_CODE, "entityTypes.addresses.postalCode" );
 123  0
                 criteriaConversion.put( KIMPropertyConstants.Person.COUNTRY_CODE, "entityTypes.addresses.countryCode" );
 124  0
                 criteriaConversion.put( KIMPropertyConstants.Person.CAMPUS_CODE, "affiliations.campusCode" );
 125  0
                 criteriaConversion.put( KIMPropertyConstants.Person.AFFILIATION_TYPE_CODE, "affiliations.affiliationTypeCode" );
 126  0
                 criteriaConversion.put( KIMPropertyConstants.Person.EXTERNAL_IDENTIFIER_TYPE_CODE, "externalIdentifiers.externalIdentifierTypeCode" );
 127  0
                 criteriaConversion.put( KIMPropertyConstants.Person.EXTERNAL_ID, "externalIdentifiers.externalId" );                
 128  0
                 criteriaConversion.put( KIMPropertyConstants.Person.EMPLOYEE_TYPE_CODE, "employmentInformation.employeeTypeCode" );
 129  0
                 criteriaConversion.put( KIMPropertyConstants.Person.EMPLOYEE_STATUS_CODE, "employmentInformation.employeeStatusCode" );
 130  0
                 criteriaConversion.put( KIMPropertyConstants.Person.EMPLOYEE_ID, "employmentInformation.employeeId" );
 131  0
                 criteriaConversion.put( KIMPropertyConstants.Person.BASE_SALARY_AMOUNT, "employmentInformation.baseSalaryAmount" );
 132  0
                 criteriaConversion.put( KIMPropertyConstants.Person.PRIMARY_DEPARTMENT_CODE, "employmentInformation.primaryDepartmentCode" );
 133  
 
 134  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.PRINCIPAL_ID );
 135  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.PRINCIPAL_NAME );
 136  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.ENTITY_ID );
 137  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.FIRST_NAME );
 138  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.LAST_NAME );
 139  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.MIDDLE_NAME );
 140  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.CAMPUS_CODE );
 141  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.EMPLOYEE_ID );
 142  0
                 personCachePropertyNames.add( KIMPropertyConstants.Person.PRIMARY_DEPARTMENT_CODE );
 143  0
         }
 144  
 
 145  
         
 146  
         /**
 147  
          * @see org.kuali.rice.kim.service.PersonService#getPerson(java.lang.String)
 148  
          */
 149  
         public Person getPerson(String principalId) {
 150  0
                 if ( StringUtils.isBlank(principalId) ) {
 151  0
                         return null;
 152  
                 }
 153  
                 // check the cache
 154  0
                 Person person = getPersonImplFromPrincipalIdCache( principalId );
 155  0
                 if ( person != null ) {
 156  0
                         return person;
 157  
                 }
 158  0
                 EntityDefault entity = null;
 159  
                 // get the corresponding principal
 160  0
                 Principal principal = getIdentityService().getPrincipal( principalId );
 161  
                 // get the identity
 162  0
                 if ( principal != null ) {
 163  0
                         entity = getIdentityService().getEntityDefault(principal.getEntityId());
 164  
                 }
 165  
                 // convert the principal and identity to a Person
 166  
                 // skip if the person was created from the DB cache
 167  0
                 if (entity != null ) {
 168  0
                         person = convertEntityToPerson( entity, principal );
 169  0
                         addPersonToCache( person );
 170  
                 }
 171  0
                 return person;
 172  
         }
 173  
 
 174  
         protected PersonImpl convertEntityToPerson( EntityDefault entity, Principal principal ) {
 175  
                 try {
 176  
                         // get the EntityEntityType for the EntityType corresponding to a Person
 177  0
                         for ( String entityTypeCode : personEntityTypeCodes ) {
 178  0
                                 EntityTypeDataDefault entType = entity.getEntityType( entityTypeCode );
 179  
                                 // if no "person" identity type present for the given principal, skip to the next type in the list
 180  0
                                 if ( entType == null ) {
 181  0
                                         continue;
 182  
                                 }
 183  
                                 // attach the principal and identity objects
 184  
                                 // PersonImpl has logic to pull the needed elements from the KimEntity-related classes
 185  0
                                 return new PersonImpl( principal, entity, entityTypeCode );
 186  
                         }
 187  0
                         return null;
 188  0
                 } catch ( Exception ex ) {
 189  
                         // allow runtime exceptions to pass through
 190  0
                         if ( ex instanceof RuntimeException ) {
 191  0
                                 throw (RuntimeException)ex;
 192  
                         }
 193  0
                         throw new RuntimeException( "Problem building person object", ex );
 194  
                 }
 195  
         }
 196  
         
 197  
         protected Person getPersonImplFromPrincipalNameCache( String principalName ) {
 198  0
                 return personCache.get( "principalName="+principalName );
 199  
         }
 200  
 
 201  
         protected Person getPersonImplFromPrincipalIdCache( String principalId ) {
 202  0
                 return personCache.get( "principalId="+principalId );
 203  
         }
 204  
         
 205  
         protected Person getPersonImplFromEmployeeIdCache( String principalId ) {
 206  0
                 return personCache.get( "employeeId="+principalId );
 207  
         }
 208  
         
 209  
         protected void addPersonToCache( Person person ) {
 210  0
                 if ( person != null ) {
 211  0
                         synchronized (personCache) {
 212  0
                                 personCache.put( "principalName="+person.getPrincipalName(), person );
 213  0
                                 personCache.put( "principalId="+person.getPrincipalId(), person );
 214  0
                                 personCache.put( "employeeId="+person.getEmployeeId(), person );
 215  0
                         }
 216  
                 }
 217  0
         }
 218  
         
 219  
         public void flushPersonCaches() {
 220  0
             personCache.clear();
 221  0
         }
 222  
         
 223  
         
 224  
         /**
 225  
          * @see org.kuali.rice.kim.service.PersonService#getPersonByPrincipalName(java.lang.String)
 226  
          */
 227  
         public Person getPersonByPrincipalName(String principalName) {
 228  0
                 if ( StringUtils.isBlank(principalName) ) {
 229  0
                         return null;
 230  
                 }
 231  0
                 Person person = null;
 232  
                 // check the cache                
 233  0
                 person = getPersonImplFromPrincipalNameCache( principalName );
 234  0
                 if ( person != null ) {
 235  0
                         return person;
 236  
                 }
 237  0
                 EntityDefault entity = null;
 238  
                 // get the corresponding principal
 239  0
                 Principal principal = getIdentityService().getPrincipalByPrincipalName( principalName );
 240  
                 // get the identity
 241  0
                 if ( principal != null ) {
 242  0
                         entity = getIdentityService().getEntityDefault(principal.getEntityId());
 243  
                 }
 244  
                 // convert the principal and identity to a Person
 245  0
                 if ( entity != null ) {
 246  0
                         person = convertEntityToPerson( entity, principal );
 247  
                 }
 248  0
                 addPersonToCache( person );
 249  0
                 return person;
 250  
         }
 251  
 
 252  
         public Person getPersonByEmployeeId(String employeeId) {
 253  0
                 if ( StringUtils.isBlank( employeeId  ) ) {
 254  0
                         return null;
 255  
                 }
 256  
                 
 257  0
                 Person person = getPersonImplFromEmployeeIdCache( employeeId );
 258  0
                 if ( person != null ) {
 259  0
                         return person;
 260  
                 }
 261  
                 
 262  0
                 Map<String,String> criteria = new HashMap<String,String>( 1 );
 263  0
                 criteria.put( KIMPropertyConstants.Person.EMPLOYEE_ID, employeeId );
 264  0
                 List<Person> people = findPeople( criteria ); 
 265  0
                 if ( !people.isEmpty() ) {
 266  0
                         person = people.get(0);
 267  0
                         addPersonToCache( person );
 268  
                 }
 269  0
                 return person;
 270  
         }
 271  
         
 272  
         /**
 273  
          * @see org.kuali.rice.kim.service.PersonService#findPeople(Map)
 274  
          */
 275  
         public List<Person> findPeople(Map<String, String> criteria) {
 276  0
                 return findPeople(criteria, true);
 277  
         }
 278  
         
 279  
         /**
 280  
          * @see org.kuali.rice.kim.service.PersonService#findPeople(java.util.Map, boolean)
 281  
          */
 282  
         public List<Person> findPeople(Map<String, String> criteria, boolean unbounded) {
 283  0
                 List<Person> people = null;
 284  
                 // protect from NPEs
 285  0
                 if ( criteria == null ) {
 286  0
                         criteria = Collections.emptyMap();
 287  
                 }
 288  
                 // make a copy so it can be modified safely in this method
 289  0
                 criteria = new HashMap<String, String>( criteria );
 290  
                 
 291  
                 // extract the role lookup parameters and then remove them since later code will not know what to do with them
 292  0
                 String roleName = criteria.get( "lookupRoleName" );
 293  0
                 String namespaceCode = criteria.get( "lookupRoleNamespaceCode" );
 294  0
                 criteria.remove("lookupRoleName");
 295  0
                 criteria.remove("lookupRoleNamespaceCode");
 296  0
                 if ( StringUtils.isNotBlank(namespaceCode) && StringUtils.isNotBlank(roleName) ) {
 297  0
                         Integer searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils.getSearchResultsLimit(PersonImpl.class);
 298  0
                         int searchResultsLimitInt = Integer.MAX_VALUE;
 299  0
                         if (searchResultsLimit != null) {
 300  0
                                 searchResultsLimitInt = searchResultsLimit.intValue();
 301  
                         }
 302  0
                         if ( LOG.isDebugEnabled() ) {
 303  0
                                 LOG.debug("Performing Person search including role filter: " + namespaceCode + "/" + roleName );
 304  
                         }
 305  0
                         if ( criteria.size() == 1 && criteria.containsKey(KIMPropertyConstants.Person.ACTIVE) ) { // if only active is specified
 306  0
                                 if ( LOG.isDebugEnabled() ) {
 307  0
                                         LOG.debug( "Only active criteria specified, running role search first" );
 308  
                                 }
 309  
                                 // in this case, run the role lookup first and pass those results to the person lookup
 310  0
                                 Collection<String> principalIds = getRoleService().getRoleMemberPrincipalIds(namespaceCode, roleName, null);
 311  0
                                 StringBuffer sb = new StringBuffer(principalIds.size()*15);
 312  0
                                 Iterator<String> pi = principalIds.iterator();
 313  0
                                 while ( pi.hasNext() ) {
 314  0
                                         sb.append( pi.next() );
 315  0
                                         if ( pi.hasNext() ) sb.append( '|' );
 316  
                                 }
 317  
                                 // add the list of principal IDs to the lookup so that only matching Person objects are returned
 318  0
                                 criteria.put( KIMPropertyConstants.Person.PRINCIPAL_ID, sb.toString() );
 319  0
                                 people = findPeopleInternal(criteria, false); // can allow internal method to filter here since no more filtering necessary                                
 320  0
                         } else if ( !criteria.isEmpty() ) { // i.e., person criteria are specified
 321  0
                                 if ( LOG.isDebugEnabled() ) {
 322  0
                                         LOG.debug( "Person criteria also specified, running that search first" );
 323  
                                 }
 324  
                                 // run the person lookup first
 325  0
                                 people = findPeopleInternal(criteria, true); // get all, since may need to be filtered
 326  
                                 // TODO - now check if these people have the given role
 327  
                                 // build a principal list
 328  0
                                 List<String> principalIds = peopleToPrincipalIds( people );
 329  
                                 // get sublist of principals that have the given roles
 330  0
                                 principalIds = getRoleService().getPrincipalIdSubListWithRole(principalIds, namespaceCode, roleName, null);
 331  
                                 // re-convert into people objects, wrapping in CollectionIncomplete if needed
 332  0
                                 if ( !unbounded && principalIds.size() > searchResultsLimitInt ) {
 333  0
                                         int actualResultSize = principalIds.size();
 334  
                                         // trim the list down before converting to people
 335  0
                                         principalIds = new ArrayList<String>(principalIds).subList(0, searchResultsLimitInt); // yes, this is a little wasteful
 336  0
                                         people = getPeople(principalIds); // convert the results to people
 337  0
                                         people = new CollectionIncomplete<Person>( people.subList(0, searchResultsLimitInt), new Long(actualResultSize) );
 338  0
                                 } else {
 339  0
                                         people = getPeople(principalIds);
 340  
                                 }
 341  0
                         } else { // only role criteria specified
 342  0
                                 if ( LOG.isDebugEnabled() ) {
 343  0
                                         LOG.debug( "No Person criteria specified - only using role service." );
 344  
                                 }
 345  
                                 // run the role criteria to get the principals with the role
 346  0
                                 Collection<String> principalIds = getRoleService().getRoleMemberPrincipalIds(namespaceCode, roleName, null);
 347  0
                                 if ( !unbounded && principalIds.size() > searchResultsLimitInt ) {
 348  0
                                         int actualResultSize = principalIds.size();
 349  
                                         // trim the list down before converting to people
 350  0
                                         principalIds = new ArrayList<String>(principalIds).subList(0, searchResultsLimitInt); // yes, this is a little wasteful
 351  0
                                         people = getPeople(principalIds); // convert the results to people
 352  0
                                         people = new CollectionIncomplete<Person>( people.subList(0, searchResultsLimitInt), new Long(actualResultSize) );
 353  0
                                 } else {
 354  0
                                         people = getPeople(principalIds); // convert the results to people
 355  
                                 }
 356  
                         }
 357  0
                 } else {
 358  0
                         if ( LOG.isDebugEnabled() ) {
 359  0
                                 LOG.debug( "No Role criteria specified, running person lookup as normal." );
 360  
                         }
 361  0
                         people = findPeopleInternal(criteria, unbounded);
 362  
                 }
 363  0
                 return people;
 364  
         }
 365  
         
 366  
         @SuppressWarnings("unchecked")
 367  
         protected List<Person> findPeopleInternal(Map<String,String> criteria, boolean unbounded ) {
 368  
                 // convert the criteria to a form that can be used by the ORM layer
 369  
 
 370  
         //TODO convert this to the new criteria predicates
 371  0
                 Map<String,String> entityCriteria = convertPersonPropertiesToEntityProperties( criteria );
 372  
 
 373  0
         QueryByCriteria.Builder queryBuilder = QueryByCriteria.Builder.create();
 374  0
         List<Predicate> predicates = new ArrayList<Predicate>();
 375  0
         for (String key : entityCriteria.keySet()) {
 376  
             // hack for OR'd criteria
 377  0
             if (entityCriteria.get(key).contains("|")) {
 378  0
                 String[] values = entityCriteria.get(key).split("\\|");
 379  0
                 predicates.add(in(key, values));
 380  0
             } else {
 381  0
                 predicates.add(equal(key, entityCriteria.get(key)));
 382  
             }
 383  
         }
 384  0
         if (!predicates.isEmpty()) {
 385  0
             queryBuilder.setPredicates(and(predicates.toArray(new Predicate[] {})));
 386  
         }
 387  
 
 388  0
                 List<Person> people = new ArrayList<Person>(); 
 389  
 
 390  0
                 EntityDefaultQueryResults qr = getIdentityService().findEntityDefaults( queryBuilder.build() );
 391  
 
 392  0
                 for ( EntityDefault e : qr.getResults() ) {
 393  
                         // get to get all principals for the identity as well
 394  0
                         for ( Principal p : e.getPrincipals() ) {
 395  0
                                 people.add( convertEntityToPerson( e, p ) );
 396  
                         }
 397  
                 }
 398  
 
 399  0
                 return people;
 400  
         }
 401  
 
 402  
         public Map<String,String> convertPersonPropertiesToEntityProperties( Map<String,String> criteria ) {
 403  0
                 if ( LOG.isDebugEnabled() ) {
 404  0
                         LOG.debug( "convertPersonPropertiesToEntityProperties: " + criteria );
 405  
                 }
 406  0
                 boolean nameCriteria = false;
 407  0
                 boolean addressCriteria = false;
 408  0
                 boolean externalIdentifierCriteria = false;
 409  0
                 boolean affiliationCriteria = false;
 410  0
                 boolean affiliationDefaultOnlyCriteria = false;
 411  0
                 boolean phoneCriteria = false;
 412  0
                 boolean emailCriteria = false;
 413  0
                 boolean employeeIdCriteria = false;
 414  
                 // add base lookups for all person lookups
 415  0
                 HashMap<String,String> newCriteria = new HashMap<String,String>();
 416  0
                 newCriteria.putAll( baseLookupCriteria );
 417  
 
 418  0
                 newCriteria.put( "entityTypes.entityTypeCode", personEntityTypeLookupCriteria );
 419  
 
 420  0
         if ( criteria != null ) {
 421  0
                         for ( String key : criteria.keySet() ) {
 422  
                                                 
 423  
                                 //check active radio button
 424  0
                                 if(key.equals(KIMPropertyConstants.Person.ACTIVE)) {
 425  0
                                         newCriteria.put(KIMPropertyConstants.Person.ACTIVE, criteria.get(KIMPropertyConstants.Person.ACTIVE));
 426  
                                 }
 427  
                         
 428  
                                 // if no value was passed, skip the entry in the Map
 429  0
                                 if ( StringUtils.isEmpty( criteria.get(key) ) ) {
 430  0
                                         continue;
 431  
                                 }
 432  
                                 // check if the value needs to be encrypted
 433  
                                 // handle encrypted external identifiers
 434  0
                                 if ( key.equals( KIMPropertyConstants.Person.EXTERNAL_ID ) && StringUtils.isNotBlank(criteria.get(key)) ) {
 435  
                                         // look for a ext ID type property
 436  0
                                         if ( criteria.containsKey( KIMPropertyConstants.Person.EXTERNAL_IDENTIFIER_TYPE_CODE ) ) {
 437  0
                                                 String extIdTypeCode = criteria.get(KIMPropertyConstants.Person.EXTERNAL_IDENTIFIER_TYPE_CODE);
 438  0
                                                 if ( StringUtils.isNotBlank(extIdTypeCode) ) {
 439  
                                                         // if found, load that external ID Type via service
 440  0
                                                         EntityExternalIdentifierType extIdType = getIdentityService().getExternalIdentifierType(extIdTypeCode);
 441  
                                                         // if that type needs to be encrypted, encrypt the value in the criteria map
 442  0
                                                         if ( extIdType != null && extIdType.isEncryptionRequired() ) {
 443  
                                                                 try {
 444  0
                                                                         criteria.put(key, 
 445  
                                                                                         CoreApiServiceLocator.getEncryptionService().encrypt(criteria.get(key))
 446  
                                                                                         );
 447  0
                                                                 } catch (GeneralSecurityException ex) {
 448  0
                                                                         LOG.error("Unable to encrypt value for external ID search of type " + extIdTypeCode, ex );
 449  0
                                                                 }                                                                
 450  
                                                         }
 451  
                                                 }
 452  
                                         }
 453  
                                 }
 454  
                                 
 455  
                                 // convert the property to the Entity data model
 456  0
                                 String entityProperty = criteriaConversion.get( key );
 457  0
                                 if ( entityProperty != null ) {
 458  0
                                         newCriteria.put( entityProperty, criteria.get( key ) );
 459  
                                 } else {
 460  0
                                         entityProperty = key;
 461  
                                         // just pass it through if no translation present
 462  0
                                         newCriteria.put( key, criteria.get( key ) );
 463  
                                 }
 464  
                                 // check if additional criteria are needed based on the types of properties specified
 465  0
                                 if ( isNameEntityCriteria( entityProperty ) ) {
 466  0
                                         nameCriteria = true;
 467  
                                 }
 468  0
                                 if ( isExternalIdentifierEntityCriteria( entityProperty ) ) {
 469  0
                                         externalIdentifierCriteria = true;
 470  
                                 }
 471  0
                                 if ( isAffiliationEntityCriteria( entityProperty ) ) {
 472  0
                                         affiliationCriteria = true;
 473  
                                 }
 474  0
                                 if ( isAddressEntityCriteria( entityProperty ) ) {
 475  0
                                         addressCriteria = true;
 476  
                                 }
 477  0
                                 if ( isPhoneEntityCriteria( entityProperty ) ) {
 478  0
                                         phoneCriteria = true;
 479  
                                 }
 480  0
                                 if ( isEmailEntityCriteria( entityProperty ) ) {
 481  0
                                         emailCriteria = true;
 482  
                                 }
 483  0
                                 if ( isEmployeeIdEntityCriteria( entityProperty ) ) {
 484  0
                                         employeeIdCriteria = true;
 485  
                                 }                                
 486  
                                 // special handling for the campus code, since that forces the query to look
 487  
                                 // at the default affiliation record only
 488  0
                                 if ( key.equals( "campusCode" ) ) {
 489  0
                                         affiliationDefaultOnlyCriteria = true;
 490  
                                 }
 491  0
                         }                
 492  0
                         if ( nameCriteria ) {
 493  0
                                 newCriteria.put( ENTITY_NAME_PROPERTY_PREFIX + "active", "Y" );
 494  0
                                 newCriteria.put( ENTITY_NAME_PROPERTY_PREFIX + "defaultValue", "Y" );
 495  
                                 //newCriteria.put(ENTITY_NAME_PROPERTY_PREFIX + "nameTypeCode", "PRFR");//so we only display 1 result
 496  
                         }
 497  0
                         if ( addressCriteria ) {
 498  0
                                 newCriteria.put( ENTITY_ADDRESS_PROPERTY_PREFIX + "active", "Y" );
 499  0
                                 newCriteria.put( ENTITY_ADDRESS_PROPERTY_PREFIX + "defaultValue", "Y" );
 500  
                         }
 501  0
                         if ( phoneCriteria ) {
 502  0
                                 newCriteria.put( ENTITY_PHONE_PROPERTY_PREFIX + "active", "Y" );
 503  0
                                 newCriteria.put( ENTITY_PHONE_PROPERTY_PREFIX + "defaultValue", "Y" );
 504  
                         }
 505  0
                         if ( emailCriteria ) {
 506  0
                                 newCriteria.put( ENTITY_EMAIL_PROPERTY_PREFIX + "active", "Y" );
 507  0
                                 newCriteria.put( ENTITY_EMAIL_PROPERTY_PREFIX + "defaultValue", "Y" );
 508  
                         }
 509  0
                         if ( employeeIdCriteria ) {
 510  0
                                 newCriteria.put( ENTITY_EMPLOYEE_ID_PROPERTY_PREFIX + "active", "Y" );
 511  0
                                 newCriteria.put( ENTITY_EMPLOYEE_ID_PROPERTY_PREFIX + "primary", "Y" );
 512  
                         }
 513  0
                         if ( affiliationCriteria ) {
 514  0
                                 newCriteria.put( ENTITY_AFFILIATION_PROPERTY_PREFIX + "active", "Y" );
 515  
                         }
 516  0
                         if ( affiliationDefaultOnlyCriteria ) {
 517  0
                                 newCriteria.put( ENTITY_AFFILIATION_PROPERTY_PREFIX + "defaultValue", "Y" );
 518  
                         }
 519  
                 }
 520  
                 
 521  0
                 if ( LOG.isDebugEnabled() ) {
 522  0
                         LOG.debug( "Converted: " + newCriteria );
 523  
                 }
 524  0
                 return newCriteria;                
 525  
         }
 526  
 
 527  
         protected boolean isNameEntityCriteria( String propertyName ) {
 528  0
                 return propertyName.startsWith( ENTITY_NAME_PROPERTY_PREFIX );
 529  
         }
 530  
         protected boolean isAddressEntityCriteria( String propertyName ) {
 531  0
                 return propertyName.startsWith( ENTITY_ADDRESS_PROPERTY_PREFIX );
 532  
         }
 533  
         protected boolean isPhoneEntityCriteria( String propertyName ) {
 534  0
                 return propertyName.startsWith( ENTITY_PHONE_PROPERTY_PREFIX );
 535  
         }
 536  
         protected boolean isEmailEntityCriteria( String propertyName ) {
 537  0
                 return propertyName.startsWith( ENTITY_EMAIL_PROPERTY_PREFIX );
 538  
         }
 539  
         protected boolean isEmployeeIdEntityCriteria( String propertyName ) {
 540  0
                 return propertyName.startsWith( ENTITY_EMPLOYEE_ID_PROPERTY_PREFIX );
 541  
         }
 542  
         protected boolean isAffiliationEntityCriteria( String propertyName ) {
 543  0
                 return propertyName.startsWith( ENTITY_AFFILIATION_PROPERTY_PREFIX );
 544  
         }
 545  
         protected boolean isExternalIdentifierEntityCriteria( String propertyName ) {
 546  0
                 return propertyName.startsWith( ENTITY_EXT_ID_PROPERTY_PREFIX );
 547  
         }
 548  
         
 549  
         /**
 550  
          * Get the entityTypeCode that can be associated with a Person.  This will determine
 551  
          * where EntityType-related data is pulled from within the KimEntity object.  The codes
 552  
          * in the list will be examined in the order present.
 553  
          */
 554  
         public List<String> getPersonEntityTypeCodes() {
 555  0
                 return this.personEntityTypeCodes;
 556  
         }
 557  
 
 558  
         public void setPersonEntityTypeCodes(List<String> personEntityTypeCodes) {
 559  0
                 this.personEntityTypeCodes = personEntityTypeCodes;
 560  0
                 personEntityTypeLookupCriteria = null;
 561  0
                 for ( String entityTypeCode : personEntityTypeCodes ) {
 562  0
                         if ( personEntityTypeLookupCriteria == null ) {
 563  0
                                 personEntityTypeLookupCriteria = entityTypeCode;
 564  
                         } else {
 565  0
                                 personEntityTypeLookupCriteria = personEntityTypeLookupCriteria + "|" + entityTypeCode;
 566  
                         }
 567  
                 }
 568  0
         }
 569  
 
 570  
         
 571  
         protected List<Person> getPeople( Collection<String> principalIds ) {
 572  0
                 List<Person> people = new ArrayList<Person>( principalIds.size() );
 573  0
                 for ( String principalId : principalIds ) {
 574  0
                         people.add( getPerson(principalId) );
 575  
                 }
 576  0
                 return people;
 577  
         }
 578  
         
 579  
         protected List<String> peopleToPrincipalIds( List<Person> people ) {
 580  0
                 List<String> principalIds = new ArrayList<String>();
 581  
                 
 582  0
                 for ( Person person : people ) {
 583  0
                         principalIds.add( person.getPrincipalId() );
 584  
                 }
 585  
                 
 586  0
                 return principalIds;
 587  
         }
 588  
         
 589  
         /**
 590  
          * @see org.kuali.rice.kim.service.PersonService#getPersonByExternalIdentifier(java.lang.String, java.lang.String)
 591  
          */
 592  
         public List<Person> getPersonByExternalIdentifier(String externalIdentifierTypeCode, String externalId) {
 593  0
                 if (StringUtils.isBlank( externalIdentifierTypeCode ) || StringUtils.isBlank( externalId ) ) {
 594  0
                         return null;
 595  
                 }
 596  0
                 Map<String,String> criteria = new HashMap<String,String>( 2 );
 597  0
                 criteria.put( KIMPropertyConstants.Person.EXTERNAL_IDENTIFIER_TYPE_CODE, externalIdentifierTypeCode );
 598  0
                 criteria.put( KIMPropertyConstants.Person.EXTERNAL_ID, externalId );
 599  0
                 return findPeople( criteria );
 600  
         }
 601  
         
 602  
         /**
 603  
          * @see org.kuali.rice.kim.service.PersonService#updatePersonIfNecessary(java.lang.String, org.kuali.rice.kim.bo.Person)
 604  
          */
 605  
     public Person updatePersonIfNecessary(String sourcePrincipalId, Person currentPerson ) {
 606  0
         if (currentPerson  == null // no person set
 607  
                 || !StringUtils.equals(sourcePrincipalId, currentPerson.getPrincipalId() ) // principal ID mismatch
 608  
                 || currentPerson.getEntityId() == null ) { // syntheticially created Person object
 609  0
             Person person = getPerson( sourcePrincipalId );
 610  
             // if a synthetically created person object is present, leave it - required for property derivation and the UI layer for
 611  
             // setting the principal name
 612  0
             if ( person == null ) {
 613  0
                 if ( currentPerson != null && currentPerson.getEntityId() == null ) {
 614  0
                     return currentPerson;
 615  
                 }
 616  
             }
 617  
             // if both are null, create an empty object for property derivation
 618  0
             if ( person == null && currentPerson == null ) {
 619  
                     try {
 620  0
                             return new PersonImpl();
 621  0
                     } catch ( Exception ex ) {
 622  0
                             LOG.error( "unable to instantiate an object of type: " + getPersonImplementationClass() + " - returning null", ex );
 623  0
                             return null;
 624  
                     }
 625  
             }
 626  0
             return person;
 627  
         }
 628  
         // otherwise, no need to change the given object
 629  0
         return currentPerson;
 630  
     }
 631  
 
 632  
     /**
 633  
      * Builds a map containing entries from the passed in Map that do NOT represent properties on an embedded
 634  
      * Person object.
 635  
      */
 636  
     private Map<String,String> getNonPersonSearchCriteria( BusinessObject bo, Map<String,String> fieldValues) {
 637  0
         Map<String,String> nonUniversalUserSearchCriteria = new HashMap<String,String>();
 638  0
         for ( String propertyName : fieldValues.keySet() ) {
 639  0
             if (!isPersonProperty(bo, propertyName)) {
 640  0
                 nonUniversalUserSearchCriteria.put(propertyName, fieldValues.get(propertyName));
 641  
             }
 642  
         }
 643  0
         return nonUniversalUserSearchCriteria;
 644  
     }
 645  
 
 646  
 
 647  
     private boolean isPersonProperty(BusinessObject bo, String propertyName) {
 648  
         try {
 649  0
                 if ( ObjectUtils.isNestedAttribute( propertyName ) // is a nested property
 650  
                             && !StringUtils.contains(propertyName, "add.") ) {// exclude add line properties (due to path parsing problems in PropertyUtils.getPropertyType)
 651  0
                         Class<?> type = PropertyUtils.getPropertyType(bo, ObjectUtils.getNestedAttributePrefix( propertyName ));
 652  
                         // property type indicates a Person object
 653  0
                         if ( type != null ) {
 654  0
                                 return Person.class.isAssignableFrom(type);
 655  
                         }
 656  0
                         LOG.warn( "Unable to determine type of nested property: " + bo.getClass().getName() + " / " + propertyName );
 657  
                 }
 658  0
         } catch (Exception ex) {
 659  0
                 if ( LOG.isDebugEnabled() ) {
 660  0
                         LOG.debug("Unable to determine if property on " + bo.getClass().getName() + " to a person object: " + propertyName, ex );
 661  
                 }
 662  0
         }
 663  0
         return false;
 664  
     }
 665  
     
 666  
     /**
 667  
      * @see org.kuali.rice.kim.service.PersonService#hasPersonProperty(java.lang.Class, java.util.Map)
 668  
      */
 669  
     public boolean hasPersonProperty(Class<? extends BusinessObject> businessObjectClass, Map<String,String> fieldValues) {
 670  0
             if ( businessObjectClass == null || fieldValues == null ) {
 671  0
                     return false;
 672  
             }
 673  
             try {
 674  0
                     BusinessObject bo = businessObjectClass.newInstance();
 675  0
                 for ( String propertyName : fieldValues.keySet() ) {
 676  0
                     if (isPersonProperty(bo, propertyName)) {
 677  0
                             return true;
 678  
                     }
 679  
                 }
 680  0
             } catch (Exception ex) {
 681  0
                     if ( LOG.isDebugEnabled() ) {
 682  0
                             LOG.debug( "Error instantiating business object class passed into hasPersonProperty", ex );
 683  
                     }
 684  
                         // do nothing
 685  0
                 }
 686  0
         return false;
 687  
     }    
 688  
 
 689  
     /**
 690  
      * @see org.kuali.rice.kim.service.PersonService#resolvePrincipalNamesToPrincipalIds(org.kuali.rice.krad.bo.BusinessObject, java.util.Map)
 691  
      */
 692  
     @SuppressWarnings("unchecked")
 693  
         public Map<String,String> resolvePrincipalNamesToPrincipalIds(BusinessObject businessObject, Map<String,String> fieldValues) {
 694  0
             if ( fieldValues == null ) {
 695  0
                     return null;
 696  
             }
 697  0
             if ( businessObject == null ) {
 698  0
                     return fieldValues;
 699  
             }
 700  0
             StringBuffer resolvedPrincipalIdPropertyName = new StringBuffer();
 701  
             // save off all criteria which are not references to Person properties
 702  
             // leave person properties out so they can be resolved and replaced by this method
 703  0
         Map<String,String> processedFieldValues = getNonPersonSearchCriteria(businessObject, fieldValues);
 704  0
         for ( String propertyName : fieldValues.keySet() ) {                
 705  0
             if (        !StringUtils.isBlank(fieldValues.get(propertyName))  // property has a value
 706  
                             && isPersonProperty(businessObject, propertyName) // is a property on a Person object
 707  
                             ) {
 708  
                     // strip off the prefix on the property
 709  0
                 String personPropertyName = ObjectUtils.getNestedAttributePrimitive( propertyName );
 710  
                 // special case - the user ID 
 711  0
                 if ( StringUtils.equals( KIMPropertyConstants.Person.PRINCIPAL_NAME, personPropertyName) ) {
 712  0
                     Class targetBusinessObjectClass = null;
 713  0
                     BusinessObject targetBusinessObject = null;
 714  0
                     resolvedPrincipalIdPropertyName.setLength( 0 ); // clear the buffer without requiring a new object allocation on each iteration
 715  
                         // get the property name up until the ".principalName"
 716  
                         // this should be a reference to the Person object attached to the BusinessObject                        
 717  0
                         String personReferenceObjectPropertyName = ObjectUtils.getNestedAttributePrefix( propertyName );
 718  
                         // check if the person was nested within another BO under the master BO.  If so, go up one more level
 719  
                         // otherwise, use the passed in BO class as the target class
 720  0
                     if ( ObjectUtils.isNestedAttribute( personReferenceObjectPropertyName ) ) {
 721  0
                         String targetBusinessObjectPropertyName = ObjectUtils.getNestedAttributePrefix( personReferenceObjectPropertyName );
 722  0
                         targetBusinessObject = (BusinessObject)ObjectUtils.getPropertyValue( businessObject, targetBusinessObjectPropertyName );
 723  0
                         if (targetBusinessObject != null) {
 724  0
                             targetBusinessObjectClass = targetBusinessObject.getClass();
 725  0
                             resolvedPrincipalIdPropertyName.append(targetBusinessObjectPropertyName).append(".");
 726  
                         } else {
 727  0
                             LOG.error("Could not find target property '"+propertyName+"' in class "+businessObject.getClass().getName()+". Property value was null.");
 728  
                         }
 729  0
                     } else { // not a nested Person property
 730  0
                         targetBusinessObjectClass = businessObject.getClass();
 731  0
                         targetBusinessObject = businessObject;
 732  
                     }
 733  
                     
 734  0
                     if (targetBusinessObjectClass != null) {
 735  
                             // use the relationship metadata in the KNS to determine the property on the
 736  
                             // host business object to put back into the map now that the principal ID
 737  
                             // (the value stored in application tables) has been resolved
 738  0
                         String propName = ObjectUtils.getNestedAttributePrimitive( personReferenceObjectPropertyName );
 739  0
                         BusinessObjectRelationship rel = getBusinessObjectMetaDataService().getBusinessObjectRelationship( targetBusinessObject, propName );
 740  0
                         if ( rel != null ) {
 741  0
                             String sourcePrimitivePropertyName = rel.getParentAttributeForChildAttribute(KIMPropertyConstants.Person.PRINCIPAL_ID);
 742  0
                             resolvedPrincipalIdPropertyName.append(sourcePrimitivePropertyName);
 743  
                                 // get the principal - for translation of the principalName to principalId
 744  0
                             String principalName = fieldValues.get( propertyName );
 745  0
                                 Principal principal = getIdentityService().getPrincipalByPrincipalName( principalName );
 746  0
                             if (principal != null ) {
 747  0
                                 processedFieldValues.put(resolvedPrincipalIdPropertyName.toString(), principal.getPrincipalId());
 748  
                             } else {
 749  0
                                 processedFieldValues.put(resolvedPrincipalIdPropertyName.toString(), null);
 750  
                                 try {
 751  
                                     // if the principalName is bad, then we need to clear out the Person object
 752  
                                     // and base principalId property
 753  
                                     // so that their values are no longer accidentally used or re-populate
 754  
                                     // the object
 755  0
                                     ObjectUtils.setObjectProperty(targetBusinessObject, resolvedPrincipalIdPropertyName.toString(), null );
 756  0
                                     ObjectUtils.setObjectProperty(targetBusinessObject, propName, null );
 757  0
                                     ObjectUtils.setObjectProperty(targetBusinessObject, propName + ".principalName", principalName );
 758  0
                                 } catch ( Exception ex ) {
 759  0
                                     LOG.error( "Unable to blank out the person object after finding that the person with the given principalName does not exist.", ex );
 760  0
                                 }
 761  
                             }
 762  0
                         } else {
 763  0
                                 LOG.error( "Missing relationship for " + propName + " on " + targetBusinessObjectClass.getName() );
 764  
                         }
 765  0
                     } else { // no target BO class - the code below probably will not work
 766  0
                         processedFieldValues.put(resolvedPrincipalIdPropertyName.toString(), null);
 767  
                     }
 768  
                 }
 769  
             // if the property does not seem to match the definition of a Person property but it
 770  
             // does end in principalName then...
 771  
             // this is to handle the case where the user ID is on an ADD line - a case excluded from isPersonProperty()
 772  0
             } else if (propertyName.endsWith("." + KIMPropertyConstants.Person.PRINCIPAL_NAME)){
 773  
                 // if we're adding to a collection and we've got the principalName; let's populate universalUser
 774  0
                 String principalName = fieldValues.get(propertyName);
 775  0
                 if ( StringUtils.isNotEmpty( principalName ) ) {
 776  0
                     String containerPropertyName = propertyName;
 777  0
                     if (containerPropertyName.startsWith(KRADConstants.MAINTENANCE_ADD_PREFIX)) {
 778  0
                         containerPropertyName = StringUtils.substringAfter( propertyName, KRADConstants.MAINTENANCE_ADD_PREFIX );
 779  
                     }
 780  
                     // get the class of the object that is referenced by the property name
 781  
                     // if this is not true then there's a principalName collection or primitive attribute 
 782  
                     // directly on the BO on the add line, so we just ignore that since something is wrong here
 783  0
                     if ( ObjectUtils.isNestedAttribute( containerPropertyName ) ) {
 784  
                             // the first part of the property is the collection name
 785  0
                         String collectionName = StringUtils.substringBefore( containerPropertyName, "." );
 786  
                         // what is the class held by that collection?
 787  
                         // JHK: I don't like this.  This assumes that this method is only used by the maintenance
 788  
                         // document service.  If that will always be the case, this method should be moved over there.
 789  0
                         Class<? extends BusinessObject> collectionBusinessObjectClass = getMaintenanceDocumentDictionaryService()
 790  
                                         .getCollectionBusinessObjectClass(
 791  
                                                         getMaintenanceDocumentDictionaryService()
 792  
                                                                         .getDocumentTypeName(businessObject.getClass()), collectionName);
 793  0
                         if (collectionBusinessObjectClass != null) {
 794  
                             // we are adding to a collection; get the relationships for that object; 
 795  
                                 // is there one for personUniversalIdentifier?
 796  0
                             List<BusinessObjectRelationship> relationships = 
 797  
                                             getBusinessObjectMetaDataService().getBusinessObjectRelationships( collectionBusinessObjectClass );
 798  
                             // JHK: this seems like a hack - looking at all relationships for a BO does not guarantee that we get the right one
 799  
                             // JHK: why not inspect the objects like above?  Is it the property path problems because of the .add. portion?
 800  0
                             for ( BusinessObjectRelationship rel : relationships ) {
 801  0
                                     String parentAttribute = rel.getParentAttributeForChildAttribute( KIMPropertyConstants.Person.PRINCIPAL_ID );
 802  0
                                     if ( parentAttribute == null ) {
 803  0
                                             continue;
 804  
                                     }
 805  
                                 // there is a relationship for personUserIdentifier; use that to find the universal user
 806  0
                                     processedFieldValues.remove( propertyName );
 807  0
                                         String fieldPrefix = StringUtils.substringBeforeLast( StringUtils.substringBeforeLast( propertyName, "." + KIMPropertyConstants.Person.PRINCIPAL_NAME ), "." );
 808  0
                                 String relatedPrincipalIdPropertyName = fieldPrefix + "." + parentAttribute;
 809  
                                 // KR-683 Special handling for extension objects
 810  0
                                           if(EXTENSION.equals(StringUtils.substringAfterLast(fieldPrefix, ".")) && EXTENSION.equals(StringUtils.substringBefore(parentAttribute, ".")))
 811  
                                           {
 812  0
                                                   relatedPrincipalIdPropertyName = fieldPrefix + "." + StringUtils.substringAfter(parentAttribute, ".");
 813  
                                           }
 814  0
                                 String currRelatedPersonPrincipalId = processedFieldValues.get(relatedPrincipalIdPropertyName);
 815  0
                                 if ( StringUtils.isBlank( currRelatedPersonPrincipalId ) ) {
 816  0
                                         Principal principal = getIdentityService().getPrincipalByPrincipalName( principalName );
 817  0
                                         if ( principal != null ) {
 818  0
                                                 processedFieldValues.put(relatedPrincipalIdPropertyName, principal.getPrincipalId());
 819  
                                         } else {
 820  0
                                                 processedFieldValues.put(relatedPrincipalIdPropertyName, null);
 821  
                                         }
 822  
                                 }
 823  0
                             } // relationship loop
 824  0
                         } else {
 825  0
                                 if ( LOG.isDebugEnabled() ) {
 826  0
                                         LOG.debug( "Unable to determine class for collection referenced as part of property: " + containerPropertyName + " on " + businessObject.getClass().getName() );
 827  
                                 }
 828  
                         }
 829  0
                     } else {
 830  0
                             if ( LOG.isDebugEnabled() ) {
 831  0
                                     LOG.debug( "Non-nested property ending with 'principalName': " + containerPropertyName + " on " + businessObject.getClass().getName() );
 832  
                             }
 833  
                     }
 834  
                 }
 835  0
             }
 836  
         }
 837  0
         return processedFieldValues;
 838  
     }
 839  
         
 840  
         // OTHER METHODS
 841  
 
 842  
         protected IdentityService getIdentityService() {
 843  0
                 if ( identityService == null ) {
 844  0
                         identityService = KimApiServiceLocator.getIdentityService();
 845  
                 }
 846  0
                 return identityService;
 847  
         }
 848  
 
 849  
         protected RoleService getRoleService() {
 850  0
                 if ( roleService == null ) {
 851  0
                         roleService = KimApiServiceLocator.getRoleService();
 852  
                 }
 853  0
                 return roleService;
 854  
         }
 855  
 
 856  
 
 857  
         public Class<? extends Person> getPersonImplementationClass() {
 858  0
                 return PersonImpl.class;
 859  
         }
 860  
         
 861  
         protected BusinessObjectMetaDataService getBusinessObjectMetaDataService() {
 862  0
                 if ( businessObjectMetaDataService == null ) {
 863  0
                         businessObjectMetaDataService = KNSServiceLocator.getBusinessObjectMetaDataService();
 864  
                 }
 865  0
                 return businessObjectMetaDataService;
 866  
         }
 867  
 
 868  
         protected MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
 869  0
                 if ( maintenanceDocumentDictionaryService == null ) {
 870  0
                         maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
 871  
                 }
 872  0
                 return maintenanceDocumentDictionaryService;
 873  
         }
 874  
 
 875  
         public void setPersonCacheMaxSize(int personCacheMaxSize) {
 876  0
                 this.personCacheMaxSize = personCacheMaxSize;
 877  0
         }
 878  
 
 879  
         public void setPersonCacheMaxAgeSeconds(int personCacheMaxAgeSeconds) {
 880  0
                 this.personCacheMaxAgeSeconds = personCacheMaxAgeSeconds;
 881  0
         }
 882  
 
 883  
     @Override
 884  
     public void afterPropertiesSet() throws Exception {
 885  0
         personCache = new MapMaker().expireAfterAccess(personCacheMaxAgeSeconds, TimeUnit.SECONDS).maximumSize(personCacheMaxSize).softValues().makeMap();
 886  0
     }
 887  
 }