/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.jboss.cdi.tck.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.jboss.cdi.tck.api.Configuration;
import org.jboss.cdi.tck.spi.Beans;
import org.jboss.cdi.tck.spi.Contexts;
import org.jboss.cdi.tck.spi.EL;
/**
* CDI TCK configuration builder.
*
* Mostly based on jboss-test-harness code from <code>org.jboss.testharness.impl.PropertiesBasedConfigurationBuilder</code> and
* <code>org.jboss.testharness.properties.PropertiesManager</code>.
*/
public class PropertiesBasedConfigurationBuilder {
public static final String RESOURCE_BUNDLE = "META-INF/cdi-tck.properties";
private final Configuration configuration;
/**
*
*/
public PropertiesBasedConfigurationBuilder() {
configuration = new ConfigurationImpl();
}
/**
* @return built configuration
*/
public Configuration getConfiguration() {
return configuration;
}
/**
* @param deploymentPhase Deployment phase (building test archive) initialization includes deployment specific properties
* @return initialized self
*/
@SuppressWarnings("unchecked")
public PropertiesBasedConfigurationBuilder init(boolean deploymentPhase) {
configuration.setBeans(getInstanceValue(Beans.PROPERTY_NAME, Beans.class, !deploymentPhase));
configuration.setEl(getInstanceValue(EL.PROPERTY_NAME, EL.class, !deploymentPhase));
configuration.setContexts((Contexts<?>)getInstanceValue(Contexts.PROPERTY_NAME, Contexts.class, !deploymentPhase));
configuration.setLibraryDirectory(getStringValue(Configuration.LIBRARY_DIRECTORY_PROPERTY_NAME, null, deploymentPhase));
configuration.setTestDataSource(getStringValue(Configuration.TEST_DATASOURCE_PROPERTY_NAME, null, deploymentPhase));
configuration.setTestJmsConnectionFactory(getStringValue(Configuration.TEST_JMS_CONNECTION_FACTORY, null, deploymentPhase));
configuration.setTestJmsQueue(getStringValue(Configuration.TEST_JMS_QUEUE, null, deploymentPhase));
configuration.setTestJmsTopic(getStringValue(Configuration.TEST_JMS_TOPIC, null, deploymentPhase));
configuration.setTestTimeoutFactor(getIntegerValue(Configuration.TEST_TIMEOUT_FACTOR, Configuration.TEST_TIMEOUT_FACTOR_DEFAULT_VALUE, false));
return this;
}
/**
* Get a list of possible values for a given key.
*
* First, System properties are tried, followed by the specified resource bundle (first in classpath only).
*
* @param key The key to search for
* @return A list of possible values. An empty list is returned if there are no matches.
*/
public Set<String> getPropertyValues(String key) {
Set<String> values = new HashSet<String>();
addPropertiesFromSystem(key, values);
addPropertiesFromResourceBundle(key, values);
return values;
}
/**
*
* @param propertyName
* @param defaultValue
* @param required
* @return
*/
public String getStringValue(String propertyName, String defaultValue, boolean required) {
String value = getValue(propertyName, required);
return value != null ? value : defaultValue;
}
/**
*
* @param propertyName
* @param defaultValue
* @param required
* @return
*/
public long getLongValue(String propertyName, long defaultValue, boolean required) {
try {
String value = getValue(propertyName, required);
return value != null ? Long.valueOf(value) : defaultValue;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid property value", e);
}
}
/**
*
* @param propertyName
* @param defaultValue
* @param required
* @return
*/
public int getIntegerValue(String propertyName, int defaultValue, boolean required) {
try {
String value = getValue(propertyName, required);
return value != null ? Integer.valueOf(value) : defaultValue;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid property value", e);
}
}
/**
* Adds matches from system properties
*
* @param key The key to match
* @param values The currently found values
*/
private void addPropertiesFromSystem(String key, Set<String> values) {
addProperty(key, System.getProperty(key), values);
}
/**
* Adds matches from detected resource bundles
*
* @param key The key to match
* @param values The currently found values
*/
private void addPropertiesFromResourceBundle(String key, Set<String> values) {
try {
for (Enumeration<URL> e = getResources(RESOURCE_BUNDLE); e.hasMoreElements();) {
URL url = e.nextElement();
Properties properties = new Properties();
InputStream propertyStream = url.openStream();
try {
properties.load(propertyStream);
addProperty(key, properties.getProperty(key), values);
} finally {
if (propertyStream != null) {
propertyStream.close();
}
}
}
} catch (IOException e) {
// No-op, file is optional
}
}
/**
* Add the property to the set of properties only if it hasn't already been added
*
* @param key The key searched for
* @param value The value of the property
* @param values The currently found values
*/
private void addProperty(String key, String value, Set<String> values) {
if (value != null) {
values.add(value);
}
}
/**
*
* @param name
* @return
* @throws IOException
*/
public Enumeration<URL> getResources(String name) throws IOException {
if (Thread.currentThread().getContextClassLoader() != null) {
return Thread.currentThread().getContextClassLoader().getResources(name);
} else {
return getClass().getClassLoader().getResources(name);
}
}
/**
*
* @param <T>
* @param propertyName
* @param expectedType
* @param required
* @return
*/
protected <T> T getInstanceValue(String propertyName, Class<T> expectedType, boolean required) {
T instance = null;
Class<T> clazz = getClassValue(propertyName, expectedType, required);
if (clazz != null) {
try {
instance = clazz.newInstance();
} catch (InstantiationException e) {
throw new IllegalStateException("Error instantiating " + clazz + " specified by " + propertyName, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Error instantiating " + clazz + " specified by " + propertyName, e);
}
}
return instance;
}
/**
*
* @param <T>
* @param propertyName
* @param expectedType
* @param required
* @return
*/
@SuppressWarnings("unchecked")
protected <T> Class<T> getClassValue(String propertyName, Class<T> expectedType, boolean required) {
Set<Class<T>> classes = new HashSet<Class<T>>();
for (String className : getPropertyValues(propertyName)) {
ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
try {
if (currentThreadClassLoader != null) {
classes.add((Class<T>) currentThreadClassLoader.loadClass(className));
} else {
classes.add((Class<T>) Class.forName(className));
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Implementation class with name " + className + " not found using classloader "
+ (currentThreadClassLoader != null ? currentThreadClassLoader : this.getClass().getClassLoader()), e);
}
}
if (classes.size() == 0) {
if (required) {
throw new IllegalArgumentException("Cannot find any implementations of " + expectedType.getSimpleName() + ", check that " + propertyName
+ " is specified");
} else {
return null;
}
} else if (classes.size() > 1) {
throw new IllegalArgumentException("More than one implementation of " + expectedType.getSimpleName() + " specified by " + propertyName
+ ", not sure which one to use!");
} else {
return classes.iterator().next();
}
}
private String getValue(String propertyName, boolean required) {
Set<String> values = getPropertyValues(propertyName);
if (values.size() == 0) {
if (required) {
throw new IllegalArgumentException("Cannot find required property " + propertyName + ", check that it is specified");
}
return null;
} else if (values.size() > 1) {
throw new IllegalArgumentException("More than one value given for " + propertyName + ", not sure which one to use!");
} else {
return values.iterator().next();
}
}
}