/* * Copyright 2004-2005 Revolution Systems Inc. * * 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. */ package com.revolsys.ui.web.config; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; public class DatabasePropertyOverrideConfigurer extends DatabaseConfigurer { /** * The pattern for setting the value for a key in a Map property of a bean * (e.g. bean.property[key]). */ private static final Pattern MAP_PROPERTY_VALUE_PATTERN = Pattern .compile("(\\w+)\\.(\\w+)\\[([a-z]\\w*)\\]"); /** * The pattern for setting the value for a property of a bean (e.g. * bean.property). */ private static final Pattern SIMPLE_PROPERTY_PATTERN = Pattern.compile("(\\w+)\\.(\\w+)"); /** Contains names of beans that have overrides */ private final Set<String> beanNames = Collections.synchronizedSet(new HashSet<String>()); /** * The flag indictaing if invalid keys should be ignored or an exception * thrown. */ private boolean ignoreInvalidKeys = false; /** * Apply the given property value to the corresponding map property on the * bean. * * @param factory The bean factory. * @param beanName The name of the bean. * @param propertyName The name of the property. * @param mapKey The key in the map to set. * @param value The value to set. */ protected void applyMapPropertyValue(final ConfigurableListableBeanFactory factory, final String beanName, final String propertyName, final String mapKey, final String value) { this.beanNames.add(beanName); final BeanDefinition bd = factory.getBeanDefinition(beanName); final MutablePropertyValues values = bd.getPropertyValues(); final PropertyValue propertyValue = values.getPropertyValue(propertyName); if (propertyValue != null) { final Object objectValue = propertyValue.getValue(); if (objectValue instanceof Map) { final Map map = (Map)objectValue; map.put(mapKey, value); } else { throw new BeanInitializationException( "Bean property [" + beanName + "." + propertyName + "] is not a Map"); } } else { final Map<String, Object> map = new HashMap<>(); map.put(mapKey, value); values.addPropertyValue(propertyName, map); } } /** * Apply the given property value to the corresponding bean. * * @param factory The bean factory. * @param beanName The name of the bean. * @param propertyName The name of the property. * @param value The value to set. */ protected void applyPropertyValue(final ConfigurableListableBeanFactory factory, final String beanName, final String propertyName, final String value) { this.beanNames.add(beanName); final BeanDefinition bd = factory.getBeanDefinition(beanName); final MutablePropertyValues values = bd.getPropertyValues(); final PropertyValue propertyValue = new PropertyValue(propertyName, value); propertyValue.setOptional(this.ignoreInvalidKeys); values.addPropertyValue(propertyValue); } /** * Were there overrides for this bean? Only valid after processing has * occurred at least once. * * @param beanName name of the bean to query status for * @return whether there were property overrides for the named bean */ public boolean hasPropertyOverridesFor(final String beanName) { return this.beanNames.contains(beanName); } /** * Process the given key as 'beanName.property' entry. * * @param factory The bean factory. * @param key The key used to set the property. * @param value The value to set. @ If there was an problem setting the value. */ protected void processKey(final ConfigurableListableBeanFactory factory, final String key, final String value) { final Matcher mapProperetyValueMatcher = MAP_PROPERTY_VALUE_PATTERN.matcher(key); if (mapProperetyValueMatcher.matches()) { final String beanName = mapProperetyValueMatcher.group(1); final String beanProperty = mapProperetyValueMatcher.group(2); final String mapKey = mapProperetyValueMatcher.group(3); applyMapPropertyValue(factory, beanName, beanProperty, mapKey, value); } else { final Matcher simpleMatcher = SIMPLE_PROPERTY_PATTERN.matcher(key); if (simpleMatcher.matches()) { final String beanName = simpleMatcher.group(1); final String beanProperty = simpleMatcher.group(2); applyPropertyValue(factory, beanName, beanProperty, value); } else { throw new BeanInitializationException("Invalid key [" + key + "]"); } } if (getLog().isDebugEnabled()) { getLog().debug("WebProperty '" + key + "' set to [" + value + "]"); } } /** * @param beanFactory The bean factory. * @param properties The property name and values to set. @ If there was an * problem setting the values. */ @Override protected void processProperties(final ConfigurableListableBeanFactory beanFactory, final Map properties) { for (final Iterator en = properties.keySet().iterator(); en.hasNext();) { final String key = (String)en.next(); try { processKey(beanFactory, key, (String)properties.get(key)); } catch (final BeansException ex) { final String msg = "Could not process key [" + key + "] in PropertyOverrideConfigurer"; if (this.ignoreInvalidKeys) { if (getLog().isDebugEnabled()) { getLog().debug(msg, ex); } else { getLog().warn(msg + ": " + ex.getMessage()); } } else { throw new BeanInitializationException(msg, ex); } } } } /** * <p> * Set the flag indictaing if invalid keys should be ignored or an exception * thrown. If you ignore invalid keys, keys that do not follow the * 'beanName.property' format will just be logged as warning. This allows to * have arbitrary other keys in a properties file. * </p> * * @param ignore The flag indictaing if invalid keys should be ignored or an * exception thrown. */ public void setIgnoreInvalidKeys(final boolean ignore) { this.ignoreInvalidKeys = ignore; } }