/* * Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute * Copyright [2016-2017] EMBL-European Bioinformatics Institute * * 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.ensembl.healthcheck.configurationmanager; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationHandler; import uk.co.flamingpenguin.jewel.cli.ArgumentValidationException; import uk.co.flamingpenguin.jewel.cli.CliFactory; import java.util.List; import java.util.ArrayList; /** * Use for creating different kinds of configuration objects, supports * * - Properties * - Commandline and * - Cascading * * from the static enum ConfigurationType in this class. * * The interface of the configuration objects returned follows the example * of uk.co.flamingpenguin.jewel.cli. The concrete interface that this * factories configuration objects should implement must be passed to the * constructor as a class together with the command line arguments. * * @author mnuhn * */ public class ConfigurationFactory <T> { /** * An enumeration of the kinds of configuration objects that the * configuration object can produce. * */ public static enum ConfigurationType { Properties, Commandline, Cascading } /** * Configuration objects will return an object of this type. */ protected final Class<T> configurationInterface; /** * A list of configuration objects that will be used for producing a * Cascading configuration object */ protected final List<T> configurationObjects; /** * A list of property files from which configuration objects based on * Properties are produced. */ protected final List<File> propertyFile; /** * The command line arguments as they would be given in to the main method * of any java program. * */ protected final String[] commandLineArguments; /** * @param configurationInterface The class object of the interface that * should be mocked by the produced configuration objects * * @param args The command line parameters that were given. These will * be used to initialise the created objects and to find properties * files that the user specified. */ public ConfigurationFactory(Class<T> configurationInterface, Object ... args) { this.configurationInterface = configurationInterface; List<String> commandLineArguments = new ArrayList<String>(); List<T> configurationObjects = new ArrayList<T>(); List<File> propertyFiles = new ArrayList<File>(); // Can be a configuration object or a String for (Object arg : args) { if (arg != null) { if (arg instanceof String) { // Every string in the constructor is a command line argument commandLineArguments.add((String) arg); } else { if (arg instanceof String[]) { // // The constructor accepts arrays of Strings as well like // the args[] that are passed to the main method. These // are added to the list of command line arguments. // String[] argList = (String[]) arg; for (String a : argList) { commandLineArguments.add(a); } } else { if (arg instanceof List<?>) { propertyFiles.addAll( (List<File>) arg ); } else { configurationObjects.add((T) arg); } } } } } this.commandLineArguments = commandLineArguments.toArray(new String[]{}); this.configurationObjects = configurationObjects; this.propertyFile = propertyFiles; } /** * @param configurationType element of the enum ConfigurationTypes specifying what * configuration object you want. * @return ConfigurationUserParameters * * The factory method that creates configuration objects. * */ public T getConfiguration(ConfigurationType configurationType) { T conf = null; if (configurationType == configurationType.Properties) { if (propertyFile.size() == 0) { throw new RuntimeException("No property file was given!"); } if (propertyFile.size() == 1) { return getConfigurationByProperties(this.propertyFile.get(0)); } if (propertyFile.size() > 1) { throw new RuntimeException("More than one property file was given, this is ambiguous!"); } } if (configurationType == configurationType.Commandline) { return getConfigurationByCommandline(); } if (configurationType == configurationType.Cascading) { return getConfigurationByCascading(); } throw new IllegalArgumentException(); } /** * @return T * * Create a configuration object that returns parameters read from a * property file. * */ protected List<T> getConfigurationByProperties(List<File> propertyFiles) { List<T> configurationList = new ArrayList<T>(); for (File file : propertyFiles) { configurationList.add(getConfigurationByProperties(file)); } return configurationList; } protected T getConfigurationByProperties(File propertyFile) { InvocationHandler handler; try { handler = new ConfigurationByProperties(configurationInterface, propertyFile); } catch (IOException e) { throw new ConfigurationException( "There was a problem reading properties from file " + propertyFile.getName(), e ); } @SuppressWarnings("unchecked") T configuration = (T) java.lang.reflect.Proxy.newProxyInstance( this.configurationInterface.getClassLoader(), new Class[] { this.configurationInterface }, handler ); return configuration; } /** * * Create a configuration object that returns parameters read from a * property file. * * @return T */ protected T getConfigurationByCascading() { List<T> configurationObjectList = new ArrayList<T>(); // Add the configuration objects in the order they should override // each other. Command line options override property file information // which in turn override any default configuration objects that may // have been given. // configurationObjectList.add (getConfigurationByCommandline()); configurationObjectList.addAll (getConfigurationByProperties(this.propertyFile)); configurationObjectList.addAll (configurationObjects); InvocationHandler handler = new ConfigurationByCascading<T>(configurationObjectList); @SuppressWarnings("unchecked") T configuration = (T) java.lang.reflect.Proxy.newProxyInstance( this.configurationInterface.getClassLoader(), new Class[] { this.configurationInterface }, handler ); return configuration; } /** * Create a configuration object that returns parameters read from the * command line. * * @return T * */ protected T getConfigurationByCommandline() { T result = null; String[] args = this.commandLineArguments; if (args == null) { throw new NullPointerException("Command line arguments have not been set in factory!"); } try { result = (T) CliFactory.parseArguments( this.configurationInterface, args ); } catch (ArgumentValidationException e) { throw new ConfigurationException(e.getMessage()); } return result; } }