/**
 * Cassandra implementation of the user repository.
 *
 * @author Julien Dubois
 */
@Repository
public class CassandraUserRepository implements UserRepository {

  private final Log log = LogFactory.getLog(CassandraUserRepository.class);

  @Inject private EntityManagerImpl em;

  @Inject private Keyspace keyspaceOperator;

  @Inject private CounterRepository counterRepository;

  private static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
  private static Validator validator = factory.getValidator();

  @Override
  @CacheEvict(value = "user-cache", key = "#user.login")
  public void createUser(User user) {
    if (log.isDebugEnabled()) {
      log.debug("Creating user : "******"user-cache", key = "#user.login", beforeInvocation = true)
  public void updateUser(User user) throws ConstraintViolationException, IllegalArgumentException {
    if (log.isDebugEnabled()) {
      log.debug("Updating user : "******"user-cache", key = "#user.login")
  public void deleteUser(User user) {
    if (log.isDebugEnabled()) {
      log.debug("Deleting user : "******"user-cache")
  public User findUserByLogin(String login) {
    User user = null;
    try {
      user = em.find(User.class, login);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.debug("Exception while looking for user " + login + " : " + e.toString());
      }
      return null;
    }
    if (user != null) {
      user.setStatusCount(counterRepository.getStatusCounter(login));
      user.setFollowersCount(counterRepository.getFollowersCounter(login));
      user.setFriendsCount(counterRepository.getFriendsCounter(login));
    }
    return user;
  }
}
Beispiel #2
0
  public static Map<String, String> toRequestMap(
      PaypalClassicModel paypalClassicModel, String format) {
    Map<String, String> returnMap = new HashMap<>();
    if (paypalClassicModel == null) {
      return returnMap;
    }
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    //        ValidatorFactory factory = Validation.byDefaultProvider()
    //                .configure()
    //                .messageInterpolator( new MyMessageInterpolator() )
    //                .buildValidatorFactory();
    Validator validator = factory.getValidator();
    Set<ConstraintViolation<PaypalClassicModel>> violations =
        validator.validate(paypalClassicModel);
    if (violations.size() == 0) {
      try {
        for (Field field : FieldUtils.getAllFields(paypalClassicModel.getClass())) {
          if (field.getType().isAssignableFrom(String.class)) {
            if (BeanUtils.getProperty(paypalClassicModel, field.getName()) != null) {
              if (StringUtils.isNumeric(format)) {
                returnMap.put(
                    field.getName().toUpperCase() + format,
                    BeanUtils.getProperty(paypalClassicModel, field.getName()));
              } else {
                returnMap.put(
                    getFormatedKeyName(format, field.getName(), null),
                    BeanUtils.getProperty(paypalClassicModel, field.getName()));
              }
            }
          }
          if (PaypalClassicModel.class.isAssignableFrom(field.getType())) {

            if (PropertyUtils.getProperty(paypalClassicModel, field.getName()) != null) {
              returnMap.putAll(
                  toRequestMap(
                      (PaypalClassicModel)
                          PropertyUtils.getProperty(paypalClassicModel, field.getName()),
                      format));
            }
          }
          if (List.class.isAssignableFrom(field.getType())) {
            List listItem = (List) PropertyUtils.getProperty(paypalClassicModel, field.getName());
            if (listItem != null) {
              for (int i = 0; i < listItem.size(); i++) {
                if (listItem.get(i) instanceof PaypalClassicModel) {
                  PaypalCollection paypalCollection = field.getAnnotation(PaypalCollection.class);
                  if (paypalCollection != null
                      && StringUtils.isNotEmpty(paypalCollection.format())) {
                    String formatStr = field.getAnnotation(PaypalCollection.class).format();
                    returnMap.putAll(
                        toRequestMap(
                            (PaypalClassicModel) listItem.get(i),
                            getFormatedKeyName(formatStr, null, i)));
                  } else {
                    if (StringUtils.isNoneEmpty(format)) {
                      returnMap.putAll(
                          toRequestMap(
                              (PaypalClassicModel) listItem.get(i), format + String.valueOf(i)));
                    } else {
                      returnMap.putAll(
                          toRequestMap((PaypalClassicModel) listItem.get(i), String.valueOf(i)));
                    }
                  }
                }
                if (listItem.get(i) instanceof List) {
                  PaypalCollection paypalCollection = field.getAnnotation(PaypalCollection.class);
                  if (paypalCollection != null
                      && StringUtils.isNotEmpty(paypalCollection.format())) {
                    String formatStr = field.getAnnotation(PaypalCollection.class).format();
                    returnMap.putAll(
                        toRequestMap(
                            (List) listItem.get(i), getFormatedKeyName(formatStr, null, i)));
                  } else {
                    if (StringUtils.isNoneEmpty(format)) {
                      returnMap.putAll(
                          toRequestMap((List) listItem.get(i), format + String.valueOf(i)));
                    } else {
                      returnMap.putAll(toRequestMap((List) listItem.get(i), String.valueOf(i)));
                    }
                  }
                }
                if (listItem.get(i) instanceof String) {
                  PaypalCollection paypalCollection = field.getAnnotation(PaypalCollection.class);
                  if (paypalCollection != null
                      && StringUtils.isNotEmpty(paypalCollection.format())) {
                    String formatStr = paypalCollection.format();
                    formatStr = getFormatedKeyName(formatStr, field.getName(), i);
                    returnMap.put(
                        getFormatedKeyName(format, formatStr, null), listItem.get(i).toString());
                  } else {
                    returnMap.put(
                        getFormatedKeyName(format, field.getName(), null) + i,
                        listItem.get(i).toString());
                  }
                }
              }
            }
          }
        }
      } catch (IllegalAccessException e) {
        throw new ValidationException(e.getMessage());
      } catch (InvocationTargetException e) {
        throw new ValidationException(e.getMessage());
      } catch (NoSuchMethodException e) {
        throw new ValidationException(e.getMessage());
      }
    } else {
      StringBuffer buf = new StringBuffer();
      for (ConstraintViolation<PaypalClassicModel> violation : violations) {
        buf.append(violation.getMessage() + "\n");
      }
      throw new ValidationException(buf.toString());
    }
    return returnMap;
  }
 @BeforeClass
 public static void init() {
   vf = Validation.buildDefaultValidatorFactory();
   validator = vf.getValidator();
 }
/**
 * Default implementation of the {@link PersonService}.
 *
 * @author Scott Battaglia
 * @author Dmitriy Kopylenko
 * @since 1.0.0
 */
@Named("personService")
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class DefaultPersonService implements PersonService {

  protected final Logger logger = LoggerFactory.getLogger(getClass());

  private final PersonRepository personRepository;

  private final ReferenceRepository referenceRepository;

  private final DisclosureRecalculationStrategyRepository strategyRepository;

  private final Reconciler reconciler;

  private final IdentifierGenerator identifierGenerator;

  private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

  @Resource(name = "sorRoleElector")
  private SorRoleElector sorRoleElector = new DefaultSorRoleElector();

  @Resource(name = "birthDateFieldElector")
  private FieldElector<Date> birthDateFieldElector = new DefaultBirthDateFieldElector();

  @Resource(name = "genderFieldElector")
  private FieldElector<String> genderFieldElector = new DefaultGenderFieldElector();

  @Resource(name = "preferredNameFieldElector")
  private FieldElector<SorName> preferredNameFieldElector = new DefaultNameFieldSelector();

  @Resource(name = "officialNameFieldElector")
  private FieldElector<SorName> officialNameFieldElector = new DefaultNameFieldSelector();

  @Resource(name = "personAttributesElector")
  private FieldElector<Map<String, String>> attributesElector = new DefaultAttributesElector();

  private FieldElector<EmailAddress> preferredContactEmailAddressFieldElector =
      new DefaultPreferredEmailContactFieldSelector();

  private FieldElector<Phone> preferredContactPhoneNumberFieldElector =
      new DefaultPreferredPhoneContactFieldSelector();

  @Resource(name = "disclosureFieldElector")
  private FieldElector<SorDisclosureSettings> disclosureFieldElector =
      new DefaultDisclosureSettingsFieldElector();

  @Resource(name = "ssnFieldElector")
  private FieldElector<String> ssnFieldElector = new DefaultSSNFieldElector();

  private enum RecalculationType {
    DELETE,
    ADD,
    UPDATE
  }

  @Resource(name = "personFactory")
  private ObjectFactory<Person> personObjectFactory;

  @Autowired(required = false)
  private Map<ReconciliationCriteria, ReconciliationResult> criteriaCache =
      new EhCacheBackedMapImpl<ReconciliationCriteria, ReconciliationResult>();

  @Autowired(required = false)
  private List<IdentifierAssigner> identifierAssigners = new ArrayList<IdentifierAssigner>();

  @Inject private IdentifierChangeService identifierChangeService;

  @Resource(name = "idCardGenerator")
  private IdCardGenerator idCardGenerator;

  @Inject
  public DefaultPersonService(
      final PersonRepository personRepository,
      final ReferenceRepository referenceRepository,
      final DisclosureRecalculationStrategyRepository strategyRepository,
      final IdentifierGenerator identifierGenerator,
      final Reconciler reconciler) {
    this.personRepository = personRepository;
    this.referenceRepository = referenceRepository;
    this.strategyRepository = strategyRepository;
    this.identifierGenerator = identifierGenerator;
    this.reconciler = reconciler;
  }

  public void setPreferredContactEmailAddressFieldElector(
      final FieldElector<EmailAddress> preferredContactEmailAddressFieldElector) {
    this.preferredContactEmailAddressFieldElector = preferredContactEmailAddressFieldElector;
  }

  public void setPreferredContactPhoneNumberFieldElector(
      final FieldElector<Phone> preferredContactPhoneNumberFieldElector) {
    this.preferredContactPhoneNumberFieldElector = preferredContactPhoneNumberFieldElector;
  }

  public void setPersonObjectFactory(final ObjectFactory<Person> personObjectFactory) {
    this.personObjectFactory = personObjectFactory;
  }

  public void setCriteriaCache(
      final Map<ReconciliationCriteria, ReconciliationResult> criteriaCache) {
    this.criteriaCache = criteriaCache;
  }

  public void setIdentifierAssigners(final List<IdentifierAssigner> identifierAssigners) {
    this.identifierAssigners = identifierAssigners;
  }

  public void setValidator(final Validator validator) {
    this.validator = validator;
  }

  @Transactional(readOnly = true)
  public Person findPersonById(final Long id) {
    return this.personRepository.findByInternalId(id);
  }

  @Transactional(readOnly = true)
  public Person fetchCompleteCalculatedPerson(Long id) {
    return this.personRepository.fetchCompleteCalculatedPerson(id);
  }

  @Transactional(readOnly = true)
  public Person findPersonByIdentifier(final String identifierType, final String identifierValue) {
    try {
      return this.personRepository.findByIdentifier(identifierType, identifierValue);
    } catch (final Exception e) {
      return null;
    }
  }

  @Transactional(readOnly = true)
  public SorPerson findByPersonIdAndSorIdentifier(
      final Long personId, final String sorSourceIdentifier) {
    try {
      return this.personRepository.findByPersonIdAndSorIdentifier(personId, sorSourceIdentifier);
    } catch (final Exception e) {
      logger.debug(e.getMessage(), e);
      return null;
    }
  }

  public SorPerson findBySorIdentifierAndSource(final String sorSource, final String sorId) {
    try {
      return this.personRepository.findBySorIdentifierAndSource(sorSource, sorId);
    } catch (Exception e) {
      return null;
    }
  }

  @Override
  @Transactional(readOnly = true)
  public SorPerson findByIdentifierAndSource(
      String identifierType, String identifierValue, String sorSource) {
    Person p = this.findPersonByIdentifier(identifierType, identifierValue);
    if (p == null) {
      return null;
    }
    List<SorPerson> sorPeople = this.getSorPersonsFor(p);
    for (SorPerson sorPerson : sorPeople) {
      if (sorSource.equals(sorPerson.getSourceSor())) {
        return sorPerson;
      }
    }
    return null;
  }

  @Override
  @Transactional(readOnly = true)
  public List<SorPerson> findByIdentifier(String identifierType, String identifierValue) {
    // TODO-Verify if this is the correct behavior
    Person p = this.findPersonByIdentifier(identifierType, identifierValue);
    if (p == null) {
      return null;
    }
    List<SorPerson> sorPeople = this.getSorPersonsFor(p);
    return sorPeople;
  }

  @Override
  public List<SorPerson> getSorPersonsFor(final Person person) {
    Assert.notNull(person);
    return this.personRepository.getSoRRecordsForPerson(person);
  }

  @Override
  public List<SorPerson> getSorPersonsFor(final Long personId) {
    return getSorPersonsFor(this.personRepository.findByInternalId(personId));
  }

  /**
   * This does not explicitly delete the names because its assumed the recalculation will clean it
   * up.
   */
  public boolean deleteSystemOfRecordPerson(
      final SorPerson sorPerson, final boolean mistake, final String terminationTypes) {
    Assert.notNull(sorPerson, "sorPerson cannot be null.");
    final String terminationTypeToUse =
        terminationTypes != null ? terminationTypes : Type.TerminationTypes.UNSPECIFIED.name();

    final Person person = this.personRepository.findByInternalId(sorPerson.getPersonId());
    Assert.notNull(person, "person cannot be null.");

    if (mistake) {
      Set<Role> rolesToDelete = new HashSet<Role>();

      for (final SorRole sorRole : sorPerson.getRoles()) {
        for (final Role role : person.getRoles()) {
          if (sorRole.getId().equals(role.getSorRoleId())) {
            rolesToDelete.add(role);
          }
        }
      }

      for (final Role role : rolesToDelete) {
        // let sorRoleElector delete the role and add another role if required
        sorRoleElector.removeCalculatedRole(
            person, role, this.personRepository.getSoRRecordsForPerson(person));
      }

      final Number number = this.personRepository.getCountOfSoRRecordsForPerson(person);

      if (number.intValue() == 1) {
        this.personRepository.deletePerson(person);
      }

      this.personRepository.deleteSorPerson(sorPerson);
      return true;
    }

    // we do this explicitly here because once they're gone we can't re-calculate?  We might move to
    // this to the recalculateCalculatedPerson method.
    final Type terminationReason =
        this.referenceRepository.findType(Type.DataTypes.TERMINATION, terminationTypeToUse);

    for (final SorRole sorRole : sorPerson.getRoles()) {
      for (final Role role : person.getRoles()) {
        if (!role.isTerminated() && sorRole.getId().equals(role.getSorRoleId())) {
          role.expireNow(terminationReason, true);
        }
      }
    }

    this.personRepository.deleteSorPerson(sorPerson);
    this.personRepository.savePerson(person);

    Person p = recalculatePersonBiodemInfo(person, sorPerson, RecalculationType.DELETE, mistake);
    this.personRepository.savePerson(p);
    return true;
  }

  public boolean deleteSystemOfRecordPerson(
      final String sorSource,
      final String sorId,
      final boolean mistake,
      final String terminationTypes) {
    Assert.notNull(sorSource, "sorSource cannot be null.");
    Assert.notNull(sorId, "sorId cannot be null.");
    final SorPerson sorPerson =
        this.personRepository.findBySorIdentifierAndSource(sorSource, sorId);

    return sorPerson != null && deleteSystemOfRecordPerson(sorPerson, mistake, terminationTypes);
  }

  public boolean deleteSystemOfRecordRole(
      final SorPerson sorPerson,
      final SorRole sorRole,
      final boolean mistake,
      final String terminationTypes)
      throws IllegalArgumentException {
    Assert.notNull(sorRole, "sorRole cannot be null.");
    Assert.notNull(sorPerson, "soPerson cannot be null.");
    final String terminationTypeToUse =
        terminationTypes != null ? terminationTypes : Type.TerminationTypes.UNSPECIFIED.name();

    final Person person = this.personRepository.findByInternalId(sorPerson.getPersonId());
    Assert.notNull(person, "person cannot be null.");

    final Role role = person.findRoleBySoRRoleId(sorRole.getId());
    // delete and expire role only if it exist at calculated level
    if (role != null)
      if (mistake) {

        // let SorRoleElector remove the role
        sorRoleElector.removeCalculatedRole(
            person, role, this.personRepository.getSoRRecordsForPerson(person));

      } else {
        final Type terminationReason =
            this.referenceRepository.findType(Type.DataTypes.TERMINATION, terminationTypeToUse);
        if (!role.isTerminated()) {
          role.expireNow(terminationReason, true);
        }
      }

    sorPerson.getRoles().remove(sorRole);
    this.personRepository.saveSorPerson(sorPerson);
    this.personRepository.savePerson(person);
    return true;
  }

  @PreAuthorize("hasPermission(#sorRole, 'admin')")
  public ServiceExecutionResult<SorRole> validateAndSaveRoleForSorPerson(
      final SorPerson sorPerson, final SorRole sorRole) {
    logger.info(" validateAndSaveRoleForSorPerson start");
    Assert.notNull(sorPerson, "SorPerson cannot be null.");
    Assert.notNull(sorRole, "SorRole cannot be null.");

    // check if the SoR Role has an ID assigned to it already and assign source sor
    setRoleIdAndSource(sorRole, sorPerson.getSourceSor());

    final Set validationErrors = this.validator.validate(sorRole);

    if (!validationErrors.isEmpty()) {
      // since because of existing design we cannot raise exception, we can only rollback the
      // transaction through code
      // OR-384
      if (TransactionAspectSupport.currentTransactionStatus() != null) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }

      return new GeneralServiceExecutionResult<SorRole>(validationErrors);
    }

    final SorPerson newSorPerson = this.personRepository.saveSorPerson(sorPerson);
    Person person = this.personRepository.findByInternalId(newSorPerson.getPersonId());
    final SorRole newSorRole = newSorPerson.findSorRoleBySorRoleId(sorRole.getSorId());
    // let sor role elector decide if this new role can be converted to calculated one
    sorRoleElector.addSorRole(newSorRole, person);
    person = recalculatePersonBiodemInfo(person, newSorPerson, RecalculationType.UPDATE, false);
    this.personRepository.savePerson(person);
    logger.info("validateAndSaveRoleForSorPerson end");
    return new GeneralServiceExecutionResult<SorRole>(newSorRole);
  }

  public ServiceExecutionResult<Person> validateAndSavePersonAndRole(
      final ReconciliationCriteria reconciliationCriteria) {
    logger.info(" validateAndSavePersonAndRole start");
    SorPerson sorPerson = reconciliationCriteria.getSorPerson();
    if (sorPerson == null || sorPerson.getRoles() == null)
      throw new IllegalArgumentException("Sor Person not found in provided criteria.");
    SorRole sorRole = reconciliationCriteria.getSorPerson().getRoles().get(0);
    if (sorRole == null)
      throw new IllegalArgumentException("Sor Role not found for provided criteria.");

    setRoleIdAndSource(sorRole, sorPerson.getSourceSor());

    final Set validationErrors = this.validator.validate(sorRole);

    if (!validationErrors.isEmpty()) {
      // since because of existing design we cannot raise exception, we can only rollback the
      // transaction through code
      // OR-384
      if (TransactionAspectSupport.currentTransactionStatus() != null) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }

      return new GeneralServiceExecutionResult<Person>(validationErrors);
    }

    Long personId = sorPerson.getPersonId();
    if (personId == null) {
      logger.info("calling saveSorPersonAndConvertToCalculatedPerson");
      // add new Sor Person and roles, create calculated person
      return new GeneralServiceExecutionResult<Person>(
          saveSorPersonAndConvertToCalculatedPerson(reconciliationCriteria));
    } else { // Add new Sor Person and role and link to the existing person
      Person thisPerson = this.personRepository.findByInternalId(personId);
      try {
        logger.info("calling addSorPersonAndLink");
        Person savedPerson = this.addSorPersonAndLink(reconciliationCriteria, thisPerson);
        return new GeneralServiceExecutionResult<Person>(savedPerson);
      } catch (SorPersonAlreadyExistsException sorE) {
        throw new IllegalArgumentException(
            "If a sor Person of the same source already exists, should call the other method to add the role only");
      }
    }
  }

  public ServiceExecutionResult<Person> addPerson(
      final ReconciliationCriteria reconciliationCriteria)
      throws ReconciliationException, IllegalArgumentException, SorPersonAlreadyExistsException {
    Assert.notNull(reconciliationCriteria, "reconciliationCriteria cannot be null");
    logger.info("addPerson start");
    if (reconciliationCriteria.getSorPerson().getSorId() != null
        && this.findBySorIdentifierAndSource(
                reconciliationCriteria.getSorPerson().getSourceSor(),
                reconciliationCriteria.getSorPerson().getSorId())
            != null) {
      // throw new IllegalStateException("CANNOT ADD SAME SOR RECORD.");
      throw new SorPersonAlreadyExistsException(
          this.findBySorIdentifierAndSource(
              reconciliationCriteria.getSorPerson().getSourceSor(),
              reconciliationCriteria.getSorPerson().getSorId()));
    }

    final Set validationErrors = this.validator.validate(reconciliationCriteria);

    if (!validationErrors.isEmpty()) {
      Iterator iter = validationErrors.iterator();
      while (iter.hasNext()) {
        logger.info("validation errors: " + iter.next());
      }
      // since because of existing design we cannot raise exception, we can only rollback the
      // transaction through code
      // OR-384
      if (TransactionAspectSupport.currentTransactionStatus() != null) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }

      return new GeneralServiceExecutionResult<Person>(validationErrors);
    }

    final ReconciliationResult result = this.reconciler.reconcile(reconciliationCriteria);

    switch (result.getReconciliationType()) {
      case NONE:
        return new GeneralServiceExecutionResult<Person>(
            saveSorPersonAndConvertToCalculatedPerson(reconciliationCriteria));

      case EXACT:
        return new GeneralServiceExecutionResult<Person>(
            addNewSorPersonAndLinkWithMatchedCalculatedPerson(reconciliationCriteria, result));
    }

    this.criteriaCache.put(reconciliationCriteria, result);
    logger.info("addPerson start");
    throw new ReconciliationException(result);
  }

  public ServiceExecutionResult<Person> forceAddPerson(
      final ReconciliationCriteria reconciliationCriteria)
      throws IllegalArgumentException, IllegalStateException {
    Assert.notNull(reconciliationCriteria, "reconciliationCriteria cannot be null.");
    logger.info("forceAddPerson start");
    final ReconciliationResult result = this.criteriaCache.get(reconciliationCriteria);

    if (result == null) {
      throw new IllegalStateException("No ReconciliationResult found for provided criteria.");
    }

    this.criteriaCache.remove(reconciliationCriteria);
    logger.info("forceAddPerson end");
    return new GeneralServiceExecutionResult<Person>(
        saveSorPersonAndConvertToCalculatedPerson(reconciliationCriteria));
  }

  public ServiceExecutionResult<Person> addPersonAndLink(
      final ReconciliationCriteria reconciliationCriteria, final Person person)
      throws IllegalArgumentException, IllegalStateException, SorPersonAlreadyExistsException {
    Assert.notNull(reconciliationCriteria, "reconciliationCriteria cannot be null.");
    Assert.notNull(person, "person cannot be null.");
    logger.info(" addPersonAndLink start");
    final ReconciliationResult result = this.criteriaCache.get(reconciliationCriteria);

    if (result == null) {
      throw new IllegalStateException("No ReconciliationResult found for provided criteria.");
    }

    for (final PersonMatch personMatch : result.getMatches()) {
      if (personMatch.getPerson().getId().equals(person.getId())) {
        addSorPersonAndLink(reconciliationCriteria, person);
        final Person savedPerson = this.personRepository.findByInternalId(person.getId());
        return new GeneralServiceExecutionResult<Person>(savedPerson);
      }
    }
    logger.info("addPersonAndLink end");
    throw new IllegalStateException("Person not found in ReconciliationResult.");
  }

  public ServiceExecutionResult<ReconciliationResult> reconcile(
      final ReconciliationCriteria reconciliationCriteria) throws IllegalArgumentException {
    Assert.notNull(reconciliationCriteria, "reconciliationCriteria cannot be null");
    logger.info("reconcile start");
    final Set validationErrors = this.validator.validate(reconciliationCriteria);

    if (!validationErrors.isEmpty()) {
      Iterator iter = validationErrors.iterator();
      while (iter.hasNext()) {
        logger.info("validation errors: " + iter.next());
      }
      logger.info("reconcile start");
      // since because of existing design we cannot raise exception, we can only rollback the
      // transaction through code
      // OR-384
      if (TransactionAspectSupport.currentTransactionStatus() != null) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }

      return new GeneralServiceExecutionResult<ReconciliationResult>(validationErrors);
    }

    final ReconciliationResult result = this.reconciler.reconcile(reconciliationCriteria);
    // (reconciliationCriteria, result);
    return new GeneralServiceExecutionResult<ReconciliationResult>(result);
  }

  @PostFilter("hasPermission(filterObject, 'read')")
  public List<PersonMatch> searchForPersonBy(final SearchCriteria searchCriteria) {

    if (StringUtils.hasText(searchCriteria.getIdentifierValue())) {
      final String identifierValue = searchCriteria.getIdentifierValue();
      final Person person =
          this.findPersonByIdentifier(
              searchCriteria.getIdentifierType().getName(), identifierValue);
      if (person != null)
        return new ArrayList<PersonMatch>(
            Arrays.asList(new PersonMatchImpl(person, 100, new ArrayList<FieldMatch>())));
      else return new ArrayList<PersonMatch>();
    }
    final List<Person> persons = this.personRepository.searchByCriteria(searchCriteria);
    return createMatches(persons);
  }

  /**
   * Persists an SorPerson on update.
   *
   * @param sorPerson the person to update.
   * @return serviceExecutionResult.
   */
  public ServiceExecutionResult<SorPerson> updateSorPerson(final SorPerson sorPerson) {
    final Set validationErrors = this.validator.validate(sorPerson);

    if (!validationErrors.isEmpty()) {
      Iterator iter = validationErrors.iterator();
      while (iter.hasNext()) {
        logger.info("validation errors: " + iter.next());
      }
      // since because of existing design we cannot raise exception, we can only rollback the
      // transaction through code
      // OR-384
      if (TransactionAspectSupport.currentTransactionStatus() != null) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }

      return new GeneralServiceExecutionResult<SorPerson>(validationErrors);
    }

    // do reconciliationCheck to make sure that modifications do not cause person to reconcile to a
    // different person
    if (!this.reconciler.reconcilesToSamePerson(sorPerson)) {
      throw new IllegalStateException();
    }

    // Iterate over any sorRoles setting sorid and source id if not specified by SoR.
    for (final SorRole sorRole : sorPerson.getRoles()) {
      setRoleIdAndSource(sorRole, sorPerson.getSourceSor());
    }

    // Save Sor Person
    final SorPerson savedSorPerson = this.personRepository.saveSorPerson(sorPerson);

    Person person = this.findPersonById(savedSorPerson.getPersonId());

    Assert.notNull(person, "person cannot be null.");

    logger.info(
        "Verifying Number of calculated Roles before the calculate: " + person.getRoles().size());

    // Iterate over sorRoles. SorRoles may be new or updated.
    for (final SorRole savedSorRole : savedSorPerson.getRoles()) {
      logger.info(
          "DefaultPersonService: savedSorPersonRole Found, savedSorRoleID: "
              + savedSorRole.getId());
      logger.info("DefaultPersonService: savedSorPersonRole Found, Role Must be newly added.");
      // let sor role elector decide if this new role can be converted to calculated one
      sorRoleElector.addSorRole(savedSorRole, person);
      logger.info(
          "Verifying Number of calculated Roles after calculate: " + person.getRoles().size());
    }

    for (final IdentifierAssigner ia : this.identifierAssigners) {
      ia.addIdentifierTo(sorPerson, person);
    }

    person = recalculatePersonBiodemInfo(person, savedSorPerson, RecalculationType.UPDATE, false);
    person = this.personRepository.savePerson(person);

    return new GeneralServiceExecutionResult<SorPerson>(savedSorPerson);
  }

  public ServiceExecutionResult<SorRole> updateSorRole(
      final SorPerson sorPerson, final SorRole sorRole) {
    Assert.notNull(sorPerson, "sorPerson cannot be null.");
    Assert.notNull(sorRole, "sorRole cannot be null.");

    final Set validationErrors = this.validator.validate(sorRole);

    if (!validationErrors.isEmpty()) {
      // since because of existing design we cannot raise exception, we can only rollback the
      // transaction through code
      // OR-384
      if (TransactionAspectSupport.currentTransactionStatus() != null) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }

      return new GeneralServiceExecutionResult<SorRole>(validationErrors);
    }

    final SorRole savedSorRole = this.personRepository.saveSorRole(sorRole);

    final Person person = this.personRepository.findByInternalId(sorPerson.getPersonId());
    final Role role = person.findRoleBySoRRoleId(savedSorRole.getId());
    if (role != null) {
      // update calculated role only if that role was previously converted to calculated one by
      // sorRoleElector
      role.recalculate(savedSorRole);
      this.personRepository.savePerson(person);
    }
    // else //do nothing i.e. don't update the calculated role if SorRoleElector Previously decided
    // not to convert this sor role to calculated role

    return new GeneralServiceExecutionResult<SorRole>(savedSorRole);
  }

  public boolean removeSorName(SorPerson sorPerson, Long nameId) {
    SorName name = sorPerson.findNameByNameId(nameId);
    if (name == null) return false;

    // remove name from the set (annotation in set to delete orphans)
    sorPerson.getNames().remove(name);

    // save changes
    this.personRepository.saveSorPerson(sorPerson);
    return true;
  }

  /**
   * Move All Sor Records from one person to another.
   *
   * @param fromPerson person losing sor records.
   * @param toPerson person receiving sor records.
   * @return Result of move. Validation errors if they occurred or the Person receiving sor records.
   */
  public boolean moveAllSystemOfRecordPerson(Person fromPerson, Person toPerson) {
    // get the list of sor person records that will be moving.
    List<SorPerson> sorPersonList = personRepository.getSoRRecordsForPerson(fromPerson);

    // move each sorRecord
    for (final SorPerson sorPerson : sorPersonList) {
      moveSystemOfRecordPerson(fromPerson, toPerson, sorPerson);
    }

    Set<? extends Identifier> oldIdentifiers = fromPerson.getIdentifiers();
    Set<? extends IdCard> oldIdCards = fromPerson.getIdCards();

    this.personRepository.deletePerson(fromPerson);
    logger.info("moveAllSystemOfRecordPerson: Deleted From Person");
    for (Identifier identifier : oldIdentifiers) {

      if (toPerson.getIdentifiersByType().get(identifier.getType().getName()) == null) {
        Identifier oldIdentifierAttachedTotoPerson =
            toPerson.addIdentifier(identifier.getType(), identifier.getValue());
        /// if type of this identifier don't exist then add this identifier as primary and not
        // deleted

        oldIdentifierAttachedTotoPerson.setDeleted(false);
        oldIdentifierAttachedTotoPerson.setPrimary(true);
      }
      // and if this exist then add this identifier as deleted and no primary
      else {
        Identifier oldIdentifierAttachedTotoPerson =
            toPerson.addIdentifier(identifier.getType(), identifier.getValue());
        /// if type of this identifier don't exist then add this identifier as primary and not
        // deleted

        oldIdentifierAttachedTotoPerson.setDeleted(true);
        oldIdentifierAttachedTotoPerson.setPrimary(false);
        ;
      }
    }
    for (IdCard oldIdCard : oldIdCards) {
      if (toPerson.getPrimaryIdCard() == null) {
        toPerson.addIDCard(oldIdCard);
      } else {
        if (oldIdCard.isPrimary()) oldIdCard.setPrimary(false);
        toPerson.addIDCard(oldIdCard);
      }
    }

    this.personRepository.savePerson(toPerson);

    return true;
  }

  /**
   * Move one Sor Record from one person to another.
   *
   * @param fromPerson person losing sor record.
   * @param toPerson person receiving sor record.
   * @return Success or failure.
   */
  public boolean moveSystemOfRecordPerson(
      Person fromPerson, Person toPerson, SorPerson movingSorPerson) {
    movingSorPerson.setPersonId(toPerson.getId());
    updateCalculatedPersonsOnMoveOfSor(toPerson, fromPerson, movingSorPerson);
    this.personRepository.savePerson(fromPerson);
    this.personRepository.savePerson(toPerson);
    this.personRepository.saveSorPerson(movingSorPerson);
    return true;
  }

  /**
   * Move one Sor Record from one person to another where the to person is not in the registry
   *
   * @param fromPerson person losing sor record.
   * @param movingSorPerson record that is moving.
   * @return Success or failure.
   */
  public boolean moveSystemOfRecordPersonToNewPerson(Person fromPerson, SorPerson movingSorPerson) {
    Person savedPerson =
        recalculatePersonBiodemInfo(
            this.personObjectFactory.getObject(), movingSorPerson, RecalculationType.ADD, false);
    for (final IdentifierAssigner ia : this.identifierAssigners) {
      ia.addIdentifierTo(movingSorPerson, savedPerson);
    }
    // remove identifiers assigned to new person from old person
    fromPerson.getIdentifiers().removeAll(savedPerson.getIdentifiers());
    updateCalculatedPersonsOnMoveOfSor(savedPerson, fromPerson, movingSorPerson);
    fromPerson = this.personRepository.savePerson(fromPerson);
    savedPerson = this.personRepository.savePerson(savedPerson);
    movingSorPerson.setPersonId(savedPerson.getId());
    this.personRepository.saveSorPerson(movingSorPerson);
    return true;
  }

  /**
   * Update the calculated person data. This method and updateCalculatedPersonOnDeleteOfSor need to
   * be generalized to handle recalculations.
   *
   * @param toPerson
   * @param fromPerson
   * @param sorPerson Adjust calculated roles... Point prc_role to prc_person receiving role Add the
   *     role to the set of roles in receiving prc_person Remove role from prc person losing role
   */
  protected void updateCalculatedPersonsOnMoveOfSor(
      final Person toPerson, final Person fromPerson, final SorPerson sorPerson) {
    Assert.notNull(toPerson, "toPerson cannot be null");
    Assert.notNull(fromPerson, "fromPerson cannot be null");
    logger.info("UpdateCalculated: recalculating person data for move.");

    final List<Role> rolesToDelete = new ArrayList<Role>();

    final List<SorRole> sorRoles = new ArrayList<SorRole>(sorPerson.getRoles());
    for (final SorRole sorRole : sorRoles) {
      for (final Role role : fromPerson.getRoles()) {
        if (sorRole.getId().equals(role.getSorRoleId())) {
          sorRoleElector.addSorRole(sorRole, toPerson);
          rolesToDelete.add(role);
        }
      }
    }
    for (final Role role : rolesToDelete) {
      sorRoleElector.removeCalculatedRole(
          fromPerson, role, this.personRepository.getSoRRecordsForPerson(fromPerson));
      fromPerson.getRoles().remove(role);
    }

    // TODO recalculate names for person receiving role? Anything else?
    // TODO recalculate names for person losing role? Anything else?
    //        this.personRepository.savePerson(fromPerson);
    //        this.personRepository.savePerson(toPerson);
  }

  public boolean expireRole(SorRole role) {
    role.setEnd(new Date());
    return true;
  }

  public boolean renewRole(SorRole role) {
    final Calendar cal = Calendar.getInstance();

    // TODO need to read configuration data for setting the default renewal date for this role type.
    // hard code to 6 months for now.
    cal.add(Calendar.MONTH, 6);
    role.setEnd(cal.getTime());
    return true;
  }

  protected List<PersonMatch> createMatches(final List<Person> people) {
    final List<PersonMatch> personMatches = new ArrayList<PersonMatch>();
    for (final Person person : people) {
      final PersonMatch p = new PersonMatchImpl(person, 50, new ArrayList<FieldMatch>());
      personMatches.add(p);
    }

    return personMatches;
  }

  protected Person recalculatePersonBiodemInfo(
      final Person person,
      final SorPerson sorPerson,
      final RecalculationType recalculationType,
      boolean mistake) {
    final List<SorPerson> sorPersons = this.personRepository.getSoRRecordsForPerson(person);
    logger.info("recalculatePersonBiodemInfo: start");
    if (recalculationType == RecalculationType.ADD
        || (recalculationType == RecalculationType.DELETE && !mistake)) {
      sorPersons.add(sorPerson);
    }

    copySorNamesToPerson(person, sorPersons);

    final Date birthDate =
        this.birthDateFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    final String gender =
        this.genderFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    final SorName preferredName =
        this.preferredNameFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    final SorName officialName =
        this.officialNameFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    final EmailAddress emailAddress =
        this.preferredContactEmailAddressFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    final Phone phone =
        this.preferredContactPhoneNumberFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    final Map<String, String> attributes =
        this.attributesElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    final SorDisclosureSettings disclosure =
        this.disclosureFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);

    final String ssn =
        this.ssnFieldElector.elect(
            sorPerson, sorPersons, recalculationType == RecalculationType.DELETE);
    Identifier primarySSN = person.getPrimaryIdentifiersByType().get("SSN");
    // check if the elector elcted some ssn and person does have previous ssn assigned to it
    if (!org.apache.commons.lang.StringUtils.isEmpty(ssn) && primarySSN != null) {
      try {
        this.identifierChangeService.change(person.getPrimaryIdentifiersByType().get("SSN"), ssn);
      } catch (IllegalArgumentException e) {
        logger.debug(e.getStackTrace().toString());
      } // all other exception should be propogated
    }

    person.setDateOfBirth(birthDate);
    person.setGender(gender);
    person.getPreferredContactEmailAddress().update(emailAddress);
    person.getPreferredContactPhoneNumber().update(phone);
    person.calculateDisclosureSettings(disclosure);
    person.setAttributes(attributes);

    String affiliation = "";
    Type affiliationType = null;
    if (disclosure != null) {
      logger.info(
          "after person.calculateDisclosureSettings, disclosure code : "
              + disclosure.getDisclosureCode());
    } else {
      logger.info("Disclosure is null");
    }
    List<SorRole> sorroles = sorPerson.getRoles();
    for (SorRole role : sorroles) {
      if (role != null) {
        logger.info("Role = " + role.getTitle());
        if (role.getAffiliationType() != null) {
          logger.info("Role desc= " + role.getAffiliationType().getDescription());
          affiliation = role.getAffiliationType().getDescription();

          if (person.getDisclosureSettings() != null) {
            logger.info("recalculating disclosure setting 1...");
            // person.getDisclosureSettings().recalculate(this.strategyRepository.getDisclosureRecalculationStrategy());
            person
                .getDisclosureSettings()
                .recalculate(
                    this.strategyRepository.getDisclosureRecalculationStrategy(),
                    affiliation,
                    referenceRepository);
          }
        }
      }
    }

    // SSN election is happening in the ssn identifier assigner.

    boolean preferred = false;
    boolean official = false;

    for (final Name name : person.getNames()) {
      if (!preferred && name.sameAs(preferredName)) {
        name.setPreferredName(true);
        preferred = true;
      }

      if (!official && name.sameAs(officialName)) {
        name.setOfficialName(true);
        official = true;
      }

      if (official && preferred) {
        break;
      }
    }
    logger.info("recalculatePersonBiodemInfo: end");
    //        return this.personRepository.savePerson(person);
    return person;
  }

  /**
   * Copy SorNames to Calculated Person
   *
   * @param person
   * @param sorPersons
   */
  protected void copySorNamesToPerson(final Person person, final List<SorPerson> sorPersons) {
    person.getNames().clear();

    for (final SorPerson sorPerson : sorPersons) {
      for (final SorName sorName : sorPerson.getNames()) {
        boolean alreadyAdded = false;

        for (final Name calculatedName : person.getNames()) {
          if (calculatedName.sameAs(sorName)) {
            alreadyAdded = true;
            break;
          }
        }

        if (!alreadyAdded) {
          person.addName(sorName);
        }
      }
    }
  }

  /**
   * Current workflow for converting an SorPerson into the actual Person.
   *
   * @param reconciliationCriteria the original search criteria.
   * @return the newly saved Person.
   */
  protected Person saveSorPersonAndConvertToCalculatedPerson(
      final ReconciliationCriteria reconciliationCriteria) {
    if (!StringUtils.hasText(reconciliationCriteria.getSorPerson().getSorId())) {
      reconciliationCriteria.getSorPerson().setSorId(this.identifierGenerator.generateNextString());
    }

    logger.info("Executing new code!!!!!!");

    // Iterate over any sorRoles setting sorid and source id
    for (final SorRole sorRole : reconciliationCriteria.getSorPerson().getRoles()) {
      setRoleIdAndSource(sorRole, reconciliationCriteria.getSorPerson().getSourceSor());
    }

    logger.info(
        "Creating sorPerson: person_id: " + reconciliationCriteria.getSorPerson().getPersonId());
    // Save Sor Person
    final SorPerson sorPerson =
        this.personRepository.saveSorPerson(reconciliationCriteria.getSorPerson());
    Person savedPerson =
        recalculatePersonBiodemInfo(
            this.personObjectFactory.getObject(), sorPerson, RecalculationType.ADD, false);
    savedPerson = this.personRepository.savePerson(savedPerson);

    for (final IdentifierAssigner ia : this.identifierAssigners) {
      ia.addIdentifierTo(sorPerson, savedPerson);
    }
    idCardGenerator.addIDCard(savedPerson);

    // Create calculated roles.
    for (final SorRole newSorRole : sorPerson.getRoles()) {
      // let sor role elector decide if this new role can be converted to calculated one
      sorRoleElector.addSorRole(newSorRole, savedPerson);
    }

    final Person newPerson = this.personRepository.savePerson(savedPerson);

    logger.info("Verifying Number of calculated Roles: " + newPerson.getRoles().size());

    // Now connect the SorPerson to the actual person
    sorPerson.setPersonId(newPerson.getId());
    this.personRepository.saveSorPerson(sorPerson);
    logger.info("Created sorPerson: person_id: " + sorPerson.getPersonId());

    return newPerson;
  }

  protected Person addSorPersonAndLink(
      final ReconciliationCriteria reconciliationCriteria, final Person person)
      throws SorPersonAlreadyExistsException {
    final SorPerson sorPerson = reconciliationCriteria.getSorPerson();
    final SorPerson registrySorPerson =
        this.findByPersonIdAndSorIdentifier(person.getId(), sorPerson.getSourceSor());

    if (registrySorPerson != null) {
      // throw new IllegalStateException("Oops! An error occurred. A person already exists from this
      // SoR linked to this ID!");
      throw new SorPersonAlreadyExistsException(registrySorPerson);
    }

    if (!StringUtils.hasText(sorPerson.getSorId())) {
      sorPerson.setSorId(this.identifierGenerator.generateNextString());
    }

    // Iterate over any sorRoles setting sorid and source id
    for (final SorRole sorRole : sorPerson.getRoles()) {
      setRoleIdAndSource(sorRole, sorPerson.getSourceSor());
    }

    sorPerson.setPersonId(person.getId());
    final SorPerson savedSorPerson = this.personRepository.saveSorPerson(sorPerson);

    // Create calculated roles.
    for (final SorRole newSorRole : savedSorPerson.getRoles()) {
      // let sor role elector decide if this new role can be converted to calculated one
      sorRoleElector.addSorRole(newSorRole, person);
    }

    // loop through all identifiers to see if addition of this new sor person will have a new
    // identifier created at calculated level
    for (final IdentifierAssigner ia : this.identifierAssigners) {
      ia.addIdentifierTo(sorPerson, person);
    }
    Person p = recalculatePersonBiodemInfo(person, savedSorPerson, RecalculationType.UPDATE, false);
    return this.personRepository.savePerson(p);
  }

  protected Person addNewSorPersonAndLinkWithMatchedCalculatedPerson(
      final ReconciliationCriteria reconciliationCriteria, final ReconciliationResult result)
      throws SorPersonAlreadyExistsException {
    Assert.isTrue(
        result.getMatches().size() == 1,
        "ReconciliationResult should be 'EXACT' and there should only be one person.  The result is '"
            + result.getReconciliationType()
            + "' and the number of people is "
            + result.getMatches().size()
            + ".");

    final Person person = result.getMatches().iterator().next().getPerson();
    return addSorPersonAndLink(reconciliationCriteria, person);
  }

  protected void setRoleIdAndSource(SorRole sorRole, String sorSource) {
    if (!StringUtils.hasText(sorRole.getSorId())) {
      sorRole.setSorId(this.identifierGenerator.generateNextString());
    }

    if (sorRole.getSystemOfRecord() == null) {
      sorRole.setSystemOfRecord(referenceRepository.findSystemOfRecord(sorSource));
    }
  }

  /**
   * Expose person repository to subclasses
   *
   * @return
   */
  protected PersonRepository getPersonRepository() {
    return this.personRepository;
  }

  /**
   * Expose disclosure recalculation strategy to sublclasses
   *
   * @return
   */
  protected DisclosureRecalculationStrategyRepository
      getDisclosureRecalculationStrategyRepository() {
    return this.strategyRepository;
  }

  /**
   * Expose reference repository to subclasses
   *
   * @return
   */
  protected ReferenceRepository getReferenceRepository() {
    return this.referenceRepository;
  }

  public void setIdCardGenerator(IdCardGenerator idCardGenerator) {
    this.idCardGenerator = idCardGenerator;
  }
}