All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kuali.common.util.spring.SpringUtils Maven / Gradle / Ivy

/**
 * Copyright 2010-2013 The Kuali 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/ecl2.php
 *
 * 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.kuali.common.util.spring;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.codehaus.plexus.util.StringUtils;
import org.jasypt.util.text.TextEncryptor;
import org.kuali.common.util.Assert;
import org.kuali.common.util.EncUtils;
import org.kuali.common.util.EncryptionStrength;
import org.kuali.common.util.LocationUtils;
import org.kuali.common.util.LoggerLevel;
import org.kuali.common.util.LoggerUtils;
import org.kuali.common.util.Project;
import org.kuali.common.util.ProjectUtils;
import org.kuali.common.util.PropertyUtils;
import org.kuali.common.util.Str;
import org.kuali.common.util.execute.Executable;
import org.kuali.common.util.execute.SpringExecutable;
import org.kuali.common.util.property.Constants;
import org.kuali.common.util.property.ProjectProperties;
import org.kuali.common.util.property.processor.ResolvePlaceholdersProcessor;
import org.kuali.common.util.service.DefaultSpringService;
import org.kuali.common.util.service.PropertySourceContext;
import org.kuali.common.util.service.SpringContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.PropertyPlaceholderHelper;

public class SpringUtils {

	private static final Logger logger = LoggerFactory.getLogger(SpringUtils.class);
	// Configure a helper that will fail on any unresolved placeholders
	private static final PropertyPlaceholderHelper HELPER = new PropertyPlaceholderHelper("${", "}", ":", false);

	public static Executable getSpringExecutable(Environment env, boolean skip, PropertySource ps, List> annotatedClasses) {
		/**
		 * This line creates a property source containing 100% of the properties needed by Spring to resolve any/all placeholders. It will be the only property source available to
		 * Spring so it needs to include system properties and environment variables
		 */
		PropertySourceContext psc = new PropertySourceContext(ps, true);

		// Setup the Spring context
		SpringContext context = new SpringContext();
		context.setAnnotatedClasses(annotatedClasses);
		context.setPropertySourceContext(psc);

		// Load the context
		SpringExecutable se = new SpringExecutable();
		se.setService(new DefaultSpringService());
		se.setContext(context);
		se.setSkip(skip);
		return se;
	}

	public static boolean getBoolean(Environment env, String key, boolean defaultValue) {
		String value = getProperty(env, key, defaultValue + "");
		return new Boolean(value);
	}

	public static PropertySource getPropertySource(String name, List pps) {
		// Load them from disk
		Properties source = PropertyUtils.load(pps);

		// Prepare them so they are ready for use
		prepareContextProperties(source);

		// Return a PropertySource backed by the properties
		return new PropertiesPropertySource(name, source);
	}

	public static String getRequiredResolvedProperty(Properties properties, String key) {
		return getRequiredResolvedProperty(properties, key, null);
	}

	public static String getRequiredResolvedProperty(Properties properties, String key, String defaultValue) {
		String value = properties.getProperty(key);
		value = StringUtils.isBlank(value) ? defaultValue : value;
		if (StringUtils.isBlank(value)) {
			throw new IllegalArgumentException("[" + key + "] is not set");
		} else {
			return HELPER.replacePlaceholders(value, properties);
		}
	}

	/**
	 * Process the properties passed in so they are ready for use by a Spring context.
* * 1 - Override with system/environment properties
* 2 - Decrypt any ENC(...) values
* 3 - Resolve all property values throwing an exception if any are unresolvable.
*/ public static void prepareContextProperties(Properties properties) { // Override with system/environment properties properties.putAll(PropertyUtils.getGlobalProperties()); // Are we decrypting property values? boolean decrypt = new Boolean(getRequiredResolvedProperty(properties, "properties.decrypt", "false")); if (decrypt) { // If they asked to decrypt, a password is required String password = getRequiredResolvedProperty(properties, "properties.enc.password"); // Strength is optional (defaults to BASIC) String defaultStrength = EncryptionStrength.BASIC.name(); String strength = getRequiredResolvedProperty(properties, "properties.enc.strength", defaultStrength); EncryptionStrength es = EncryptionStrength.valueOf(strength); TextEncryptor decryptor = EncUtils.getTextEncryptor(es, password); PropertyUtils.decrypt(properties, decryptor); } // Are we resolving placeholders? boolean resolve = new Boolean(getRequiredResolvedProperty(properties, "properties.resolve", "true")); if (resolve) { ResolvePlaceholdersProcessor rpp = new ResolvePlaceholdersProcessor(); rpp.setHelper(HELPER); rpp.process(properties); } } /** * Converts a GAV into Spring's classpath style notation for the default project properties context. * *
	 *  org.kuali.common:kuali-jdbc -> classpath:org/kuali/common/kuali-jdbc-properties-context.xml
	 * 
*/ public static String getDefaultPropertyContextLocation(String gav) { Assert.hasText(gav, "gav has no text"); Project p = ProjectUtils.getProject(gav); return "classpath:" + Str.getPath(p.getGroupId()) + "/" + p.getArtifactId() + "-properties-context.xml"; } /** * Make sure all of the locations actually exist */ public static void validateExists(List locations) { StringBuilder sb = new StringBuilder(); for (String location : locations) { if (!LocationUtils.exists(location)) { sb.append("Location [" + location + "] does not exist\n"); } } if (sb.length() > 0) { throw new IllegalArgumentException(sb.toString()); } } public static ConfigurableApplicationContext getContextWithPreRegisteredBeans(String id, String displayName, List beanNames, List beans) { Assert.isTrue(beanNames.size() == beans.size()); GenericXmlApplicationContext appContext = new GenericXmlApplicationContext(); if (!StringUtils.isBlank(id)) { appContext.setId(id); } if (!StringUtils.isBlank(displayName)) { appContext.setDisplayName(displayName); } appContext.refresh(); ConfigurableListableBeanFactory factory = appContext.getBeanFactory(); for (int i = 0; i < beanNames.size(); i++) { String beanName = beanNames.get(i); Object bean = beans.get(i); logger.debug("Registering bean - [{}] -> [{}]", beanName, bean.getClass().getName()); factory.registerSingleton(beanName, bean); } return appContext; } public static ConfigurableApplicationContext getContextWithPreRegisteredBeans(List beanNames, List beans) { return getContextWithPreRegisteredBeans(null, null, beanNames, beans); } /** * Null safe refresh for a context */ public static void refreshQuietly(ConfigurableApplicationContext context) { if (context != null) { context.refresh(); } } /** * Null safe close for a context */ public static void closeQuietly(ConfigurableApplicationContext context) { if (context != null) { context.close(); } } public static ConfigurableApplicationContext getContextWithPreRegisteredBean(String beanName, Object bean) { return getContextWithPreRegisteredBeans(Arrays.asList(beanName), Arrays.asList(bean)); } public static List> getPropertySources(Class annotatedClass) { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(annotatedClass); return extractPropertySourcesAndClose(context); } public static List> extractPropertySourcesAndClose(ConfigurableApplicationContext context) { // Extract PropertySources (if any) List> sources = getPropertySources(context); // Close the context closeQuietly(context); // Return the list return sources; } /** * Scan the XML Spring context for any beans that implement PropertySource */ public static List> getPropertySources(String location) { ConfigurableApplicationContext context = new GenericXmlApplicationContext(location); return extractPropertySourcesAndClose(context); } /** * This method returns a list of any PropertySource objects registered in the indicated context. They are sorted by property source name. */ public static List> getPropertySources(ConfigurableApplicationContext context) { // Sort them by name return getPropertySources(context, new PropertySourceNameComparator()); } public static Map getAllBeans(List locations, Class type) { String[] locationsArray = locations.toArray(new String[locations.size()]); ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(locationsArray); Map map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type); ctx.close(); return map; } public static Map getAllBeans(String location, Class type) { ConfigurableApplicationContext ctx = new GenericXmlApplicationContext(location); Map map = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type); ctx.close(); return map; } public static Map getAllBeans(ConfigurableApplicationContext ctx, Class type) { return BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, type); } /** * This method returns a list of any PropertySource objects registered in the indicated context. The comparator is responsible for putting them in correct order. */ public static List> getPropertySources(ConfigurableApplicationContext context, Comparator> comparator) { // Extract all beans that implement the PropertySource interface @SuppressWarnings("rawtypes") Map map = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, PropertySource.class); // Extract the PropertySource beans into a list List> list = new ArrayList>(); for (PropertySource source : map.values()) { list.add(source); } // Sort them using the provided comparator Collections.sort(list, comparator); // Return the list return list; } /** * Null safe method for converting an untyped array of property sources into a list. Never returns null. */ public static List> asList(PropertySource... sources) { List> list = new ArrayList>(); if (sources == null) { return list; } for (PropertySource element : sources) { if (element != null) { list.add(element); } } return list; } public static void debug(ApplicationContext ctx) { logger.debug("------------------------ Spring Context ------------------------------"); logger.debug("Id: [{}]", ctx.getId()); logger.debug("Display Name: [{}]", ctx.getDisplayName()); logger.debug("Application Name: [{}]", ctx.getApplicationName()); logger.debug("----------------------------------------------------------------------"); List names = Arrays.asList(BeanFactoryUtils.beanNamesIncludingAncestors(ctx)); List columns = Arrays.asList("Name", "Type", "Hashcode"); List rows = new ArrayList(); Collections.sort(names); for (String name : names) { Object bean = ctx.getBean(name); String instance = (bean == null) ? Constants.NULL : bean.getClass().getSimpleName(); String hashcode = (bean == null) ? Constants.NULL : Integer.toHexString(bean.hashCode()); Object[] row = { name, instance, hashcode }; rows.add(row); } LoggerUtils.logTable(columns, rows, LoggerLevel.DEBUG, logger, true); logger.debug("----------------------------------------------------------------------"); } public static void showPropertySources(List> propertySources) { List columns = Arrays.asList("Name", "Impl", "Source"); List rows = new ArrayList(); for (PropertySource propertySource : propertySources) { String name = propertySource.getName(); String impl = propertySource.getClass().getName(); String source = propertySource.getSource().getClass().getName(); Object[] row = { name, impl, source }; rows.add(row); } LoggerUtils.logTable(columns, rows, LoggerLevel.INFO, logger, true); } public static void showPropertySources(ConfigurableEnvironment env) { showPropertySources(getPropertySources(env)); } /** * Get a fully resolved property value from the environment. If the property is not found or contains unresolvable placeholders an exception is thrown. */ public static String getProperty(Environment env, String key) { String value = env.getRequiredProperty(key); return env.resolveRequiredPlaceholders(value); } /** * Always return a fully resolved value. Use defaultValue if a value cannot be located in the environment. Throw an exception if the return value contains * unresolvable placeholders. */ public static String getProperty(Environment env, String key, String defaultValue) { if (defaultValue == null) { // No default value supplied, we must be able to locate this property in the environment return getProperty(env, key); } else { // Look up a value from the environment String value = env.getProperty(key); if (value == null) { // Resolve the default value against the environment return env.resolveRequiredPlaceholders(defaultValue); } else { // Resolve the located value against the environment return env.resolveRequiredPlaceholders(value); } } } /** * Examine ConfigurableEnvironment for PropertySource's that extend EnumerablePropertySource and aggregate them into a single * Properties object */ public static Properties getAllEnumerableProperties(ConfigurableEnvironment env) { // Extract the list of PropertySources from the environment List> sources = getPropertySources(env); // Spring provides PropertySource objects ordered from highest priority to lowest priority // We reverse the order here so things follow the typical "last one in wins" strategy Collections.reverse(sources); // Convert the list of PropertySource's to a list of Properties objects PropertySourceConversionResult result = convertEnumerablePropertySources(sources); // Combine them into a single Properties object return PropertyUtils.combine(result.getPropertiesList()); } /** * Remove any existing property sources and add one property source backed by the properties passed in */ public static void reconfigurePropertySources(ConfigurableEnvironment env, String name, Properties properties) { // Remove all existing property sources removeAllPropertySources(env); // MutablePropertySources allow us to manipulate the list of property sources MutablePropertySources mps = env.getPropertySources(); // Make sure there are no existing property sources Assert.isTrue(mps.size() == 0); // Create a property source backed by the properties object passed in PropertiesPropertySource pps = new PropertiesPropertySource(name, properties); // Add it to the environment mps.addFirst(pps); } /** * Remove any existing property sources */ public static void removeAllPropertySources(ConfigurableEnvironment env) { MutablePropertySources mps = env.getPropertySources(); List> sources = getPropertySources(env); for (PropertySource source : sources) { String name = source.getName(); mps.remove(name); } } /** * Get all PropertySource objects from the environment as a List. */ public static List> getPropertySources(ConfigurableEnvironment env) { MutablePropertySources mps = env.getPropertySources(); List> sources = new ArrayList>(); Iterator> itr = mps.iterator(); while (itr.hasNext()) { PropertySource source = itr.next(); sources.add(source); } return sources; } /** * Convert any PropertySources that extend EnumerablePropertySource into Properties object's */ public static PropertySourceConversionResult convertEnumerablePropertySources(List> sources) { PropertySourceConversionResult result = new PropertySourceConversionResult(); List list = new ArrayList(); List> converted = new ArrayList>(); List> skipped = new ArrayList>(); // Extract property values from the sources and place them in a Properties object for (PropertySource source : sources) { logger.debug("Adding [{}]", source.getName()); if (source instanceof EnumerablePropertySource) { EnumerablePropertySource eps = (EnumerablePropertySource) source; Properties sourceProperties = convert(eps); list.add(sourceProperties); converted.add(source); } else { logger.debug("Unable to obtain properties from property source [{}] -> [{}]", source.getName(), source.getClass().getName()); skipped.add(source); } } result.setConverted(converted); result.setSkipped(skipped); result.setPropertiesList(list); return result; } /** * Convert an EnumerablePropertySource into a Properties object. */ public static Properties convert(EnumerablePropertySource source) { Properties properties = new Properties(); String[] names = source.getPropertyNames(); for (String name : names) { Object object = source.getProperty(name); if (object != null) { String value = object.toString(); properties.setProperty(name, value); } else { logger.warn("Property [{}] is null", name); } } return properties; } /** * Return true if, and only if, property is set in the environment and evaluates to true. */ public static boolean isTrue(Environment env, String property) { String value = env.getProperty(property); if (StringUtils.isBlank(value)) { return false; } else { return new Boolean(value); } } }