
org.sakaiproject.util.SakaiProperties Maven / Gradle / Ivy
/**********************************************************************************
*
* $Id: SakaiProperties.java 105077 2012-02-24 22:54:29Z [email protected] $
*
***********************************************************************************
*
* Copyright (c) 2007, 2008 Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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 org.sakaiproject.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
/**
* A configurer for "sakai.properties" files. These differ from the usual Spring default properties
* files by mixing together lines which define property-value pairs and lines which define
* bean property overrides. The two can be distinguished because Sakai conventionally uses
* the bean name separator "@" instead of the default "."
*
* This class creates separate PropertyPlaceholderConfigurer and PropertyOverrideConfigurer
* objects to handle bean configuration, and loads them with the input properties.
*
* SakaiProperties configuration supports most of the properties documented for
* PropertiesFactoryBean, PropertyPlaceholderConfigurer, and PropertyOverrideConfigurer.
*/
public class SakaiProperties implements BeanFactoryPostProcessorCreator, InitializingBean {
private static Log log = LogFactory.getLog(SakaiProperties.class);
private SakaiPropertiesFactoryBean propertiesFactoryBean = new SakaiPropertiesFactoryBean();
//private PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
private ReversiblePropertyOverrideConfigurer propertyOverrideConfigurer = new ReversiblePropertyOverrideConfigurer();
private PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
public SakaiProperties() {
// Set defaults.
propertiesFactoryBean.setIgnoreResourceNotFound(true);
propertyPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
propertyPlaceholderConfigurer.setOrder(0);
propertyOverrideConfigurer.setBeanNameAtEnd(true);
propertyOverrideConfigurer.setBeanNameSeparator("@");
propertyOverrideConfigurer.setIgnoreInvalidKeys(true);
}
public void afterPropertiesSet() throws Exception {
// Connect properties to configurers.
propertiesFactoryBean.afterPropertiesSet();
propertyPlaceholderConfigurer.setProperties((Properties)propertiesFactoryBean.getObject());
propertyOverrideConfigurer.setProperties((Properties)propertiesFactoryBean.getObject());
}
/* (non-Javadoc)
* @see org.sakaiproject.util.BeanFactoryPostProcessorCreator#getBeanFactoryPostProcessors()
*/
public Collection getBeanFactoryPostProcessors() {
return (Arrays.asList(new BeanFactoryPostProcessor[] {propertyOverrideConfigurer, propertyPlaceholderConfigurer}));
}
/**
* Gets the individual properties from each properties file which is read in
*
* @return a map of filename -> Properties
*/
public Map getSeparateProperties() {
LinkedHashMap m = new LinkedHashMap();
/* This doesn't work because spring always returns only the first of the properties files -AZ
* very disappointing because it means we can't tell which file a property came from
try {
// have to use reflection to get the fields here because Spring does not expose them directly
Field localPropertiesField = PropertiesLoaderSupport.class.getDeclaredField("localProperties");
Field locationsField = PropertiesLoaderSupport.class.getDeclaredField("locations");
localPropertiesField.setAccessible(true);
locationsField.setAccessible(true);
Properties[] localProperties = (Properties[]) localPropertiesField.get(propertiesFactoryBean);
Resource[] locations = (Resource[]) locationsField.get(propertiesFactoryBean);
log.info("found "+locations.length+" locations and "+localProperties.length+" props files");
for (int i = 0; i < localProperties.length; i++) {
Properties p = localProperties[i];
Properties props = dereferenceProperties(p);
Resource r = locations[i];
log.info("found "+p.size()+" props ("+props.size()+") in "+r.getFilename());
if (m.put(r.getFilename(), props) != null) {
log.warn("SeparateProperties: Found use of 2 sakai properties files with the same name (probable data loss): "+r.getFilename());
}
}
} catch (Exception e) {
log.warn("SeparateProperties: Failure trying to get the separate properties: "+e);
m.clear();
m.put("ALL", getProperties());
}
*/
/*
m.put("ALL", getProperties());
*/
for (Entry entry : propertiesFactoryBean.getLoadedProperties().entrySet()) {
m.put(entry.getKey(), dereferenceProperties(entry.getValue()));
}
return m;
}
/**
* INTERNAL
* @return the set of properties after processing
*/
public Properties getProperties() {
Properties rawProperties = getRawProperties();
Properties parsedProperties = dereferenceProperties(rawProperties);
return parsedProperties;
}
/**
* INTERNAL
* @return the complete set of properties exactly as read from the files
*/
public Properties getRawProperties() {
try {
return (Properties)propertiesFactoryBean.getObject();
} catch (IOException e) {
if (log.isWarnEnabled()) log.warn("Error collecting Sakai properties", e);
return new Properties();
}
}
/**
* Dereferences property placeholders in the given {@link Properties}
* in exactly the same way the {@link BeanFactoryPostProcessor}s in this
* object perform their placeholder dereferencing. Unfortunately, this
* process is not readily decoupled from the act of processing a
* bean factory in the Spring libraries. Hence the reflection.
*
* @param srcProperties a collection of name-value pairs
* @return a new collection of properties. If srcProperties
* is null
, returns null. If srcProperties
* is empty, returns a reference to same object.
* @throws RuntimeException if any aspect of processing fails
*/
private Properties dereferenceProperties(Properties srcProperties) throws RuntimeException {
if ( srcProperties == null ) {
return null;
}
if ( srcProperties.isEmpty() ) {
return srcProperties;
}
try {
Properties parsedProperties = new Properties();
PropertyPlaceholderConfigurer resolver = new PropertyPlaceholderConfigurer();
resolver.setIgnoreUnresolvablePlaceholders(true);
Method parseStringValue =
resolver.getClass().getDeclaredMethod("parseStringValue", String.class, Properties.class, Set.class);
parseStringValue.setAccessible(true);
for ( Map.Entry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy