@Override public void setAnySystemSettings( SystemSettings settings, boolean skipValidation, boolean ignoreReadOnly) { // first, we need to get the current settings so we'll know if we need to persist or merge the // new ones @SuppressWarnings("unchecked") List<SystemConfiguration> configs = entityManager.createNamedQuery(SystemConfiguration.QUERY_FIND_ALL).getResultList(); Map<String, SystemConfiguration> existingConfigMap = new HashMap<String, SystemConfiguration>(); for (SystemConfiguration config : configs) { existingConfigMap.put(config.getPropertyKey(), config); } boolean changed = false; SystemConfiguration lastUpdateTime = existingConfigMap.get(SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.getInternalName()); // verify each new setting and persist them to the database // note that if a new setting is the same as the old one, we do nothing - leave the old entity // as is for (Map.Entry<SystemSetting, String> e : settings.entrySet()) { SystemSetting prop = e.getKey(); String value = e.getValue(); if (!skipValidation) { verifyNewSystemConfigurationProperty(prop, value, settings); } SystemConfiguration existingConfig = existingConfigMap.get(prop.getInternalName()); if (e.getKey() == SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME) { // we don't let the user persist their own last system config update time // in any manner lastUpdateTime = existingConfig; } else if (existingConfig == null) { value = transformSystemConfigurationPropertyToDb(prop, value, null); existingConfig = new SystemConfiguration(prop.getInternalName(), value); entityManager.persist(existingConfig); changed = true; existingConfigMap.put(existingConfig.getPropertyKey(), existingConfig); } else { // make sure we compare the new value with a database-agnostic value // it is important to compare in the database-agnostic format instead // of database specific because the conversion isn't reflective. // Some legacy code somewhere is (or just used to) store booleans as "0"s and "1"s // even though they are stored as strings and thus don't suffer from Oracle's // lack of support for boolean data type. If we encounter such values, we convert // them to "false"/"true" and store that value to database. This is a one way operation // and therefore we need to compare the database-agnostic (i.e. "true"/"false") and // not database specific. // // More importantly though, we store the password fields obfuscated, so we need to compare // apples with // apples here. String existingValue = transformSystemConfigurationPropertyFromDb( prop, existingConfig.getPropertyValue(), true); // we need to unmask the new value so that we can compare for changes. only after that can // we transform // it into the DB format. value = unmask(prop, value, existingValue); // also for oracle, treat null and empty string as the same. if ((isEmpty(existingValue) && !isEmpty(value)) || (null != existingValue && !existingValue.equals(value))) { // SystemSetting#isReadOnly should be a superset of the "fReadOnly" field in the database // but let's just be super paranoid here... if ((prop.isReadOnly() || (existingConfig.getFreadOnly() != null && existingConfig.getFreadOnly().booleanValue())) && !(isStorageSetting(prop) || ignoreReadOnly)) { throw new IllegalArgumentException( "The setting [" + prop.getInternalName() + "] is read-only - you cannot change its current value! Current value is [" + existingConfig.getPropertyValue() + "] while the new value was [" + value + "]."); } // transform to the database-specific format value = transformSystemConfigurationPropertyToDb(prop, value, existingValue); existingConfig.setPropertyValue(value); entityManager.merge(existingConfig); changed = true; } } } if (changed) { if (lastUpdateTime == null) { lastUpdateTime = new SystemConfiguration( SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.getInternalName(), Long.toString(System.currentTimeMillis())); lastUpdateTime.setFreadOnly(SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.isReadOnly()); entityManager.persist(lastUpdateTime); } else { lastUpdateTime.setPropertyValue(Long.toString(System.currentTimeMillis())); entityManager.merge(lastUpdateTime); } existingConfigMap.put( SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.getInternalName(), lastUpdateTime); fillCache(existingConfigMap.values()); } }