/* * Copyright (c) 2005-2011 Grameen Foundation USA * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * * See also http://www.apache.org/licenses/LICENSE-2.0.html for an * explanation of the license and how it is applied. */ package org.mifos.config; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.mifos.accounts.loan.persistance.LegacyLoanDao; import org.mifos.application.servicefacade.ApplicationContextProvider; import org.mifos.config.business.MifosConfigurationManager; import org.mifos.config.exceptions.ConfigurationException; import org.mifos.config.persistence.ConfigurationPersistence; import org.mifos.config.util.helpers.ConfigConstants; import org.mifos.core.MifosRuntimeException; import org.mifos.framework.exceptions.PersistenceException; import org.mifos.framework.util.helpers.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClientRules { public static final String ClientRulesCenterHierarchyExists = "ClientRules.CenterHierarchyExists"; public static final String ClientRulesClientCanExistOutsideGroup = "ClientRules.ClientCanExistOutsideGroup"; public static final String ClientRulesGroupCanApplyLoans = "ClientRules.GroupCanApplyLoans"; public static final String ClientRulesNameSequence = "ClientRules.NameSequence"; public static final String ClientCanExistOutsideGroupKey = "ClientCanExistOutsideGroup"; public static final String GroupCanApplyLoansKey = "GroupCanApplyLoans"; public static final String AgeCheckWarningInsteadOfErrorKey = "ClientRules.AgeCheckWarningInsteadOfError"; public static final String MaximumAgeForNewClients = "ClientRules.MaximumAgeForNewClients"; public static final String MinimumAgeForNewClients = "ClientRules.MinimumAgeForNewClients"; private static Boolean centerHierarchyExists; private static Boolean groupCanApplyLoans; private static Boolean clientCanExistOutsideGroup; private static Boolean ageCheckWarningInsteadOfError; private static int minimumAgeForNewClient; private static int maximumAgeForNewClient; private static boolean ageCheckEnabled; private static int maximumNumberOfFamilyMembers; public static int getMaximumNumberOfFamilyMembers() { return maximumNumberOfFamilyMembers; } public static void setMaximumNumberOfFamilyMembers(int maximumNumberOfFamilyMembers) { ClientRules.maximumNumberOfFamilyMembers = maximumNumberOfFamilyMembers; } private static boolean familyDetailsRequired=false; public static boolean isFamilyDetailsRequired() { return familyDetailsRequired; } public static void setFamilyDetailsRequired(boolean familyDetailsRequired) { ClientRules.familyDetailsRequired = familyDetailsRequired; } /** * A name sequence is the order in which client names are displayed. * Example: first name, then middle name, then last name. * <p> * This member variable stores which of the {@link #allowedNameParts} are to * be used when displaying a client's name. */ private static String[] nameSequence; /** * Stores strings that are allowed to be part of {@link #nameSequence}. */ private static Set<String> allowedNameParts; private static final Logger logger = LoggerFactory.getLogger(ClientRules.class); private static ConfigurationPersistence configPersistence; public static ConfigurationPersistence getConfigPersistence() { if (configPersistence == null) { configPersistence = new ConfigurationPersistence(); } return configPersistence; } public static void setConfigPersistence(ConfigurationPersistence configPersistence) { ClientRules.configPersistence = configPersistence; } static { allowedNameParts = new HashSet<String>(); allowedNameParts.add(ConfigConstants.FIRST_NAME); allowedNameParts.add(ConfigConstants.MIDDLE_NAME); allowedNameParts.add(ConfigConstants.LAST_NAME); allowedNameParts.add(ConfigConstants.SECOND_LAST_NAME); } /** * Performs startup sanity checks. While not a requirement, it is considered * good practice to call this method prior to any other methods in this * class. */ public static void init() throws ConfigurationException { if (!isValidNameSequence()) { throw new ConfigurationException("error in configured value for " + ClientRulesNameSequence); } // If the configuration is invalid with respect to Client Rules, this // will force discovery of the problem upon initialization refresh(); } // "protected" visibility so it can be unit tested; it would otherwise // be private protected static void refresh() throws ConfigurationException { centerHierarchyExists = null; groupCanApplyLoans = null; ageCheckWarningInsteadOfError = null; clientCanExistOutsideGroup = null; nameSequence = null; centerHierarchyExists = getCenterHierarchyExists(); groupCanApplyLoans = getGroupCanApplyLoans(); clientCanExistOutsideGroup = getClientCanExistOutsideGroup(); ageCheckWarningInsteadOfError = isAgeCheckWarningInsteadOfErrorEnabled(); nameSequence = getNameSequence(); initializeAges(); intializeFamilyConfig(); } public static void setAgeCheckWarningInsteadOfError(boolean exists) { ageCheckWarningInsteadOfError = exists; } private static Boolean getAgeCheckWarningInsteadOfErrorFromConfig() { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); return configMgr.getBoolean(AgeCheckWarningInsteadOfErrorKey); } public static boolean isAgeCheckWarningInsteadOfErrorEnabled() { if (ageCheckWarningInsteadOfError == null) { ageCheckWarningInsteadOfError = getAgeCheckWarningInsteadOfErrorFromConfig(); } return ageCheckWarningInsteadOfError; } public static Boolean getCenterHierarchyExists() { if (centerHierarchyExists == null) { centerHierarchyExists = getCenterHierarchyExistsFromConfig(); } return centerHierarchyExists; } public static void setCenterHierarchyExists(boolean exists) { centerHierarchyExists = exists; } public static void setGroupCanApplyLoans(boolean flag) { groupCanApplyLoans = flag; } public static void setClientCanExistOutsideGroup(boolean flag){ clientCanExistOutsideGroup = flag; } /** Can group loans exist? */ public static Boolean getGroupCanApplyLoans() { if (groupCanApplyLoans == null) { groupCanApplyLoans = getGroupCanApplyLoansFromConfig(); } return groupCanApplyLoans; } public static boolean getClientCanExistOutsideGroup() { if (clientCanExistOutsideGroup == null) { clientCanExistOutsideGroup = getClientCanExistOutsideGroupFromConfig(); } return clientCanExistOutsideGroup; } private static Boolean getCenterHierarchyExistsFromConfig() { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); return configMgr.getBoolean(ClientRulesCenterHierarchyExists); } private static String getBadOverrideMsg(String key, String detailMsg) { return "The value for key " + key + " in the file " + MifosConfigurationManager.CUSTOM_CONFIG_PROPS_FILENAME + " must to be set to 1 because it was set to 1" + " in the database, hence can't be set to to 0 in the custom" + " configuration file as this might invalidate existing data. " + detailMsg + " Also, " + MifosConfigurationManager.DEFAULT_CONFIG_PROPS_FILENAME + " must never be changed--make sure this" + " file is untouched."; } private static boolean getGroupCanApplyLoansFromConfig() { boolean cfgValue; try { int dbValue = getConfigPersistence().getConfigurationValueInteger(GroupCanApplyLoansKey); MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); cfgValue = configMgr.getBoolean(ClientRulesGroupCanApplyLoans); if (dbValue == Constants.NO && cfgValue == true) { getConfigPersistence().updateConfigurationKeyValueInteger(GroupCanApplyLoansKey, Constants.YES); } else if (dbValue == Constants.YES && cfgValue == false) { if (ApplicationContextProvider.getBean(LegacyLoanDao.class).countOfGroupLoanAccounts() > 0) { // Trying to override db value of "true/yes" with "false/no" // in the config file violates business rules. throw new MifosRuntimeException(getBadOverrideMsg(GroupCanApplyLoansKey, "Group loans may already exist.")); } makeDatabaseConfigMatchFilebasedConfig(); } } catch (PersistenceException ex) { throw new MifosRuntimeException(ex); } return cfgValue; } private static void makeDatabaseConfigMatchFilebasedConfig() throws PersistenceException { getConfigPersistence().updateConfigurationKeyValueInteger(GroupCanApplyLoansKey, Constants.NO); } private static boolean getClientCanExistOutsideGroupFromConfig() { boolean cfgValue; try { int dbValue = getConfigPersistence().getConfigurationValueInteger(ClientCanExistOutsideGroupKey); MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); cfgValue = configMgr.getBoolean(ClientRulesClientCanExistOutsideGroup); if (dbValue == Constants.NO && cfgValue == true) { getConfigPersistence().updateConfigurationKeyValueInteger(ClientCanExistOutsideGroupKey, Constants.YES); } else if (dbValue == Constants.YES && cfgValue == false) { // Trying to override db value of "true/yes" with "false/no" // in the config file violates business rules. throw new MifosRuntimeException(getBadOverrideMsg(ClientCanExistOutsideGroupKey, "Clients outside of groups may already exist.")); } } catch (PersistenceException ex) { throw new MifosRuntimeException(ex); } return cfgValue; } public static void setNameSequence(String[] nameSequence) { ClientRules.nameSequence = nameSequence; } /* * Fetches and populates ClientRules#nameSequence. */ public static String[] getNameSequence() { if (nameSequence == null) { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); nameSequence = configMgr.getStringArray(ClientRulesNameSequence); } return nameSequence; } /** * Check that the given nameSequence is valid. * <p> * A name sequence is the order in which client names are displayed. * Example: first name, then middle name, then last name. * <p> * Throws no exceptions, but reasons for invalid sequences are logged as * errors. */ public static boolean isValidNameSequence(String[] nameSequence) { // a null or empty part would cause errors when a name format when, for // instance, ClientNameDetailDto.getDisplayName() is called if (null == nameSequence) { logger.error("nameSequence must not be null"); return false; } if (nameSequence.length < 1) { logger.error("nameSequence must contain elements"); return false; } // disallowed parts would cause errors when a name format when, for // instance, ClientNameDetailDto.getDisplayName() is called for (String s : nameSequence) { if (!allowedNameParts.contains(s)) { String allowed = StringUtils.join(allowedNameParts, ","); logger.error(s + " is not a known name part. Allowed values are: " + allowed + ". Any ordering is allowed."); return false; } } return true; } /** * Delegates to {@link #isValidNameSequence(String[])} to check that the * configured <i>nameSequence</i> is valid. Automatically fetches and populates * <i>nameSequence</i> if necessary. * <p> * A name sequence is the order in which client names are displayed. * Example: first name, then middle name, then last name. */ public static boolean isValidNameSequence() { return isValidNameSequence(getNameSequence()); } /* * will initialize the client minimum age and maximum age constants, Also if * both of them are set to zero it will initialize the client constants.age * check to zero */ public static void initializeAges() throws ConfigurationException { setMinimumAgeForNewClient(getMinimumAge()); setMaximumAgeForNewClient(getMaximumAge()); if (getMaximumAge() < getMinimumAge()) { throw new ConfigurationException("The minimum age for clients cannot be greater than the maximum age in " + MifosConfigurationManager.DEFAULT_CONFIG_PROPS_FILENAME); } updateAgeCheckEnabled(); } private static void updateAgeCheckEnabled() { if ((getMaximumAgeForNewClient() == 0) && (getMinimumAgeForNewClient() == 0)) { setAgeCheckEnabled(false); } else { setAgeCheckEnabled(true); } } public static int getMinimumAgeForNewClient() { return minimumAgeForNewClient; } public static void setMinimumAgeForNewClient(int minimumAgeForNewClient) { ClientRules.minimumAgeForNewClient = minimumAgeForNewClient; updateAgeCheckEnabled(); } public static int getMaximumAgeForNewClient() { return maximumAgeForNewClient; } public static void setMaximumAgeForNewClient(int maximumAgeForNewClient) { ClientRules.maximumAgeForNewClient = maximumAgeForNewClient; updateAgeCheckEnabled(); } public static boolean isAgeCheckEnabled() { return ageCheckEnabled; } public static void setAgeCheckEnabled(boolean ageCheckEnabled) { ClientRules.ageCheckEnabled = ageCheckEnabled; } /* * reads the maximum age specified in the application configuration file, * Also Checks if its in the range of 0 to 150 */ public static int getMinimumAge() throws ConfigurationException { int minimumAge = 0; MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); if (configMgr.containsKey(ClientRules.MinimumAgeForNewClients)) { minimumAge = Integer.parseInt(configMgr.getString(ClientRules.MinimumAgeForNewClients)); } else { throw new ConfigurationException("The Minimum Age for a client is not defined in " + MifosConfigurationManager.DEFAULT_CONFIG_PROPS_FILENAME); } if (minimumAge < 0 || minimumAge > 150) { throw new ConfigurationException("The Minimum Age defined in the " + MifosConfigurationManager.DEFAULT_CONFIG_PROPS_FILENAME + "is not within the acceptable range (0 to 150)"); } return minimumAge; } /* * Gets the minimum age specified in the application configuration file, * Also Checks if its in the range of 0 to 150 */ public static int getMaximumAge() throws ConfigurationException { int maximumAge = 0; MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); if (configMgr.containsKey(ClientRules.MaximumAgeForNewClients)) { maximumAge = Integer.parseInt(configMgr.getString(ClientRules.MaximumAgeForNewClients)); } else { throw new ConfigurationException("The Maximum Age for a client is not defined in " + MifosConfigurationManager.DEFAULT_CONFIG_PROPS_FILENAME); } if (maximumAge > 150 || maximumAge < 0) { throw new ConfigurationException("The Maximum Age defined in the " + MifosConfigurationManager.DEFAULT_CONFIG_PROPS_FILENAME + "is not within the acceptable range (0 to 150)"); } return maximumAge; } public static void intializeFamilyConfig() throws ConfigurationException { setFamilyDetailsRequired(ClientFamilyInfoConfig.getAreFamilyDetailsRequired()); setMaximumNumberOfFamilyMembers(ClientFamilyInfoConfig.getMaximumNumberOfFamilyMembers()); } }