/**
* Copyright (C) 2008 Mathieu Carbou <mathieu.carbou@gmail.com>
*
* 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.mycila.testing.plugins.jetty.config;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.mycila.testing.plugins.jetty.JettyRunWar;
import com.mycila.testing.plugins.jetty.NopServerLifeCycleListener;
import com.mycila.testing.plugins.jetty.ServerLifeCycleListener;
import com.mycila.testing.plugins.jetty.locator.FileLocator;
import com.mycila.testing.plugins.jetty.locator.StrategyFileLocator;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Properties;
import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.get;
import static java.lang.Boolean.parseBoolean;
import static java.lang.Integer.parseInt;
/**
* The default implementation with default values of {@link RawConfig} and provides {@link Config}uration extension.
*/
public class DefaultConfig
implements Config {
/**
* Instantiates.
*/
public DefaultConfig()
{
}
/**
* Instantiates with a {@link Properties} configuration such as keys :
* <ul>
* <li>{@code warLocation} : String # {@link #getWarLocation()}, ie : <i>ant:*.war</i></li>
* <li>{@code serverPort} : int # {@link #getServerPort()}, ie : <i>9090</i></li>
* <li>{@code contextPath} : String # {@link #getContextPath()}, ie : <i>/test</i></li>
* <li>{@code startServer} : boolean (true|false) # {@link #isStartServer()}, ie : <i>true</i></li>
* <li>{@code deployWebapp} : boolean (true|false) # {@link #isDeployWebapp()}, ie : <i>false</i></li>
* <li>{@code skip} : boolean (true|false) # {@link #isSkip()}, ie : <i>true</i></li>
* <li>{@code lifeCycleListenerClass} : String classname # {@link #getServerLifeCycleListenerClass()}, ie :
* <i>com.mycila.testing.plugins.jetty.NopServerLifeCycleListener</i></li>
* </ul>
*
* @param config
* the {@link Properties} configuration.
*
* @throws RuntimeException
* if the {@link #setServerLifeCycleListenerClass(Class)} does not exist in the VM.
* @throws NumberFormatException
* if the {@link #setServerPort(int)} is not an int.
*/
public DefaultConfig(
final Properties config)
{
try {
this.setWarLocation(config.getProperty("warLocation", DEFAULT_WAR_LOCATION));
this.setServerPort(parseInt(config.getProperty("serverPort", DEFAULT_SERVER_PORT_AS_STRING)));
this.setContextPath(config.getProperty("contextPath", DEFAULT_CONTEXT_PATH));
this.setStartServer(parseBoolean(config.getProperty("startServer", DEFAULT_START_SERVER_AS_STRING)));
this.setDeployWebapp(parseBoolean(config.getProperty("deployWebapp", DEFAULT_DEPLOY_WEBAPP_AS_STRING)));
this.setSkip(parseBoolean(config.getProperty("skip", DEFAULT_SKIP_AS_STRING)));
this.setServerLifeCycleListenerClass((Class<? extends ServerLifeCycleListener>) Class.forName(config.getProperty(
"lifeCycleListenerClass",
DEFAULT_CYCLE_LISTENER_CLASS_AS_STRING)));
}
catch (final ClassNotFoundException e) {
throw propagate(e);
}
catch (final NumberFormatException e) {
throw propagate(e);
}
}
/**
* Instantiates with a {@link RawConfig}uration;
*
* @param config
* the raw configuration;
*/
public DefaultConfig(
final RawConfig config)
{
this.setWarLocation(config.getWarLocation());
this.setContextPath(config.getContextPath());
this.setServerPort(config.getServerPort());
this.setStartServer(config.isStartServer());
this.setDeployWebapp(config.isDeployWebapp());
this.setSkip(config.isSkip());
this.setServerLifeCycleListenerClass(config.getServerLifeCycleListenerClass());
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.Config#getWarLocationUrl()
*/
public URL getWarLocationUrl()
{
try {
final FileLocator fileLocator = new StrategyFileLocator();
final File file = get(fileLocator.locate(this.warLocation), 0);
final URL url = file.toURI().toURL();
return url;
}
catch (final FileNotFoundException e) {
throw propagate(e);
}
catch (final MalformedURLException e) {
throw propagate(e);
}
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.RawConfig#getWarLocation()
*/
public String getWarLocation()
{
return this.warLocation;
}
/**
* Sets the location of the WAR file to load.
*
* @param warLocation
* the location of the WAR file to load.
*
* @see #getWarLocation()
*/
void setWarLocation(
final String warLocation)
{
this.warLocation = warLocation;
}
/**
* Returns true if {@link #getWarLocation()} value is default one, false else.
*
* @return true if {@link #getWarLocation()} value is default one, false else.
*/
boolean isDefaultWarLocation()
{
return DEFAULT_WAR_LOCATION.equals(this.warLocation);
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.RawConfig#getServerPort()
*/
public int getServerPort()
{
return this.serverPort;
}
/**
* Sets the web application server port.
*
* @param serverPort
* the web application server port.
*/
void setServerPort(
final int serverPort)
{
this.serverPort = serverPort;
}
/**
* Returns true if {@link #getServerPort()} value is default one, false else.
*
* @return true if {@link #getServerPort()} value is default one, false else.
*/
boolean isDefaultServerPort()
{
return DEFAULT_SERVER_PORT == this.serverPort;
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.RawConfig#getContextPath()
*/
public String getContextPath()
{
return this.contextPath;
}
/**
* Sets web application context path.
*
* @param contextPath
* the web application context path.
*/
void setContextPath(
final String contextPath)
{
this.contextPath = contextPath;
}
/**
* Returns true if {@link #getContextPath()} value is default one, false else.
*
* @return true if {@link #getContextPath()} value is default one, false else.
*/
boolean isDefaultContextPath()
{
return DEFAULT_CONTEXT_PATH.equals(this.contextPath);
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.RawConfig#isStartServer()
*/
public boolean isStartServer()
{
return this.startServer;
}
/**
* Sets true to start a new server (and stop the old one), false to start a server only if there is no running one.
*
* @param startServer
* true to start a new server (and stop the old one), false to start a server only if there is no running
* one.
*/
void setStartServer(
final boolean startServer)
{
this.startServer = startServer;
}
/**
* Returns true if {@link #isStartServer()} value is default one, false else.
*
* @return true if {@link #isStartServer()} value is default one, false else.
*/
boolean isDefaultStartServer()
{
return DEFAULT_START_SERVER == this.startServer;
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.RawConfig#isDeployWebapp()
*/
public boolean isDeployWebapp()
{
return this.deployWebapp;
}
/**
* Sets true to deploy a new webapp (and undeploy the old one), false to deploy a webapp only if there is no
* deployed one.
*
* @param deployWebapp
* true to deploy a new webapp (and undeploy the old one), false to deploy a webapp only if there is no
* deployed one.
*/
void setDeployWebapp(
final boolean deployWebapp)
{
this.deployWebapp = deployWebapp;
}
/**
* Returns true if {@link #isDeployWebapp()} value is default one, false else.
*
* @return true if {@link #isDeployWebapp()} value is default one, false else.
*/
boolean isDefaultDeployWebapp()
{
return DEFAULT_DEPLOY_WEBAPP == this.deployWebapp;
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.RawConfig#isSkip()
*/
public boolean isSkip()
{
return this.skip;
}
/**
* Sets true to skip starting server or deploying webapp.
*
* @param skip
* true to skip starting server or deploying webapp.
*/
void setSkip(
final boolean skip)
{
this.skip = skip;
}
/**
* Returns true if {@link #isSkip()} value is default one, false else.
*
* @return true if {@link #isSkip()} value is default one, false else.
*/
boolean isDefaultSkip()
{
return DEFAULT_SKIP == this.skip;
}
public ServerLifeCycleListener getServerLifeCycleListener(
final Map context)
{
try {
final Constructor[] constructors = this.serverLifeCycleListenerClass.getConstructors();
final ServerLifeCycleListener listener;
if (constructors.length == 1) {
listener = this.serverLifeCycleListenerClass.getConstructor().newInstance();
}
else {
final Constructor<? extends ServerLifeCycleListener> constructor = this.serverLifeCycleListenerClass.getConstructor(Map.class);
listener = constructor.newInstance(context);
}
return listener;
}
catch (final InstantiationException e) {
throw propagate(e);
}
catch (final IllegalAccessException e) {
throw propagate(e);
}
catch (final NoSuchMethodException e) {
throw propagate(e);
}
catch (InvocationTargetException e) {
throw propagate(e);
}
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.RawConfig#getServerLifeCycleListenerClass()
*/
public Class<? extends ServerLifeCycleListener> getServerLifeCycleListenerClass()
{
return this.serverLifeCycleListenerClass;
}
/**
* Sets the ServerLifeCycleListener class which allow customization of the server configuration.
*
* @param serverLifeCycleListenerClass
* the ServerLifeCycleListener class which allow customization of the server configuration.
*/
void setServerLifeCycleListenerClass(
final Class<? extends ServerLifeCycleListener> serverLifeCycleListenerClass)
{
this.serverLifeCycleListenerClass = serverLifeCycleListenerClass;
}
/**
* Returns true if {@link #getServerLifeCycleListenerClass()} value is default one, false else.
*
* @return true if {@link #getServerLifeCycleListenerClass()} value is default one, false else.
*/
boolean isDefaultServerLifeCycleListenerClass()
{
return DEFAULT_CYCLE_LISTENER_CLASS.equals(this.serverLifeCycleListenerClass);
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.Config#getSourceConfig()
*/
public JettyRunWar getSourceConfig()
{
return this.sourceConfig;
}
/**
* Sets the {@link JettyRunWar} source configuration.
*
* @param sourceConfig
* the {@link JettyRunWar} source configuration.
*/
void setSourceConfig(
final JettyRunWar sourceConfig)
{
this.sourceConfig = sourceConfig;
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.Config#getSourceMethod()
*/
public Method getSourceMethod()
{
return this.sourceMethod;
}
/**
* Sets the {@link JettyRunWar} source method.
*
* @param sourceMethod
* the {@link JettyRunWar} source method.
*/
void setSourceMethod(
final Method sourceMethod)
{
this.sourceMethod = sourceMethod;
}
/**
* {@inheritDoc}
*
* @see com.mycila.testing.plugins.jetty.config.Config#getSourceClass()
*/
public Class<?> getSourceClass()
{
return this.sourceClass;
}
/**
* Sets the {@link JettyRunWar} source class.
*
* @param sourceClass
* the {@link JettyRunWar} source class.
*/
void setSourceClass(
final Class<?> sourceClass)
{
this.sourceClass = sourceClass;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
final ToStringHelper toString = Objects.toStringHelper(this);
toString.add("warLocation", this.getWarLocation());
toString.add("serverPort", Integer.toString(this.getServerPort()));
toString.add("contextPath", this.getContextPath());
toString.add("doStartServer", Boolean.toString(this.isStartServer()));
toString.add("doDeployWebapp", Boolean.toString(this.isDeployWebapp()));
toString.add("skip", Boolean.toString(this.isSkip()));
toString.add("serverLifeCycleListenerClass", this.getServerLifeCycleListenerClass());
return toString.toString();
}
/**
* Returns true if the {@code method} is annotation with {@link JettyRunWar}, false else.
*
* @param method
* the method to test if annotated with {@link JettyRunWar}.
*
* @return true if the {@code method} is annotation with {@link JettyRunWar}, false else.
*/
public static boolean hasJettyPlugin(
final Method method)
{
final boolean hasMethodAnno = method.isAnnotationPresent(JettyRunWar.class);
final boolean hasClassAnno = hasJettyPlugin(method.getDeclaringClass());
final boolean hasPlugin = (hasMethodAnno && hasClassAnno) || hasClassAnno;
return hasPlugin;
}
/**
* Returns true if the {@code klass} is annotation with {@link JettyRunWar}, false else.
*
* @param klass
* the class to test if annotated with {@link JettyRunWar}.
*
* @return true if the {@code klass} is annotation with {@link JettyRunWar}, false else.
*/
public static boolean hasJettyPlugin(
final Class<?> klass)
{
final boolean hasClassAnno = klass.isAnnotationPresent(JettyRunWar.class);
return hasClassAnno;
}
/**
* Returns the {@link JettyRunWar} {@link Config}uration for the {@code method}.
*
* @param method
* the method for which returns the {@link JettyRunWar} {@link Config}uration.
*
* @return the {@link JettyRunWar} {@link Config}uration.
*/
public static Config configFrom(
final Method method)
{
if (!hasJettyPlugin(method)) {
throw new IllegalArgumentException("at least " + method.getDeclaringClass() + " should be annotated with "
+ JettyRunWar.class);
}
final JettyRunWar methodAnno = method.getAnnotation(JettyRunWar.class);
final boolean isMethodDefaultConfig = DefaultConfig.DEFAULT_CONFIG_CLASS.equals((methodAnno == null)
? null
: methodAnno.value());
final JettyRunWar classAnno = method.getDeclaringClass().getAnnotation(JettyRunWar.class);
final boolean isClassDefaultConfig = DefaultConfig.DEFAULT_CONFIG_CLASS.equals((classAnno == null)
? null
: classAnno.value());
if (isClassDefaultConfig && isMethodDefaultConfig) {
throw new IllegalArgumentException(method + " should not be annotated with " + JettyRunWar.class
+ " because it has the same configuration has its class " + method.getDeclaringClass());
}
if (!isClassDefaultConfig && isMethodDefaultConfig) {
throw new IllegalArgumentException(method + " must be annotated with " + JettyRunWar.class
+ " and has a custom configuration because its class " + method.getDeclaringClass()
+ " has a custom configuration");
}
final JettyRunWar sourceConfig = firstNonNull(methodAnno, classAnno);
return configFrom(sourceConfig, method, method.getDeclaringClass());
}
/**
* Returns the {@link JettyRunWar} {@link Config}uration for the {@code klass}.
*
* @param klass
* the class for which returns the {@link JettyRunWar} {@link Config}uration.
*
* @return the {@link JettyRunWar} {@link Config}uration.
*/
public static Config configFrom(
final Class<?> klass)
{
if (!hasJettyPlugin(klass)) {
throw new IllegalArgumentException("at least " + klass + " should be annotated with " + JettyRunWar.class);
}
final JettyRunWar classAnno = klass.getAnnotation(JettyRunWar.class);
return configFrom(classAnno, null, klass);
}
private static DefaultConfig configFrom(
final JettyRunWar sourceConfig,
final Method method,
final Class<?> klass)
{
try {
final RawConfig rawConfig = sourceConfig.value().newInstance();
final DefaultConfig config = new DefaultConfig(rawConfig);
config.setSourceConfig(sourceConfig);
config.setSourceMethod(method);
config.setSourceClass(klass);
return config;
}
catch (final InstantiationException e) {
throw propagate(e);
}
catch (final IllegalAccessException e) {
throw propagate(e);
}
}
/**
* The default {@link JettyRunWar#value()} {@link RawConfig}uration class.
*/
public static final Class<? extends RawConfig> DEFAULT_CONFIG_CLASS = DefaultConfig.class;
/**
* The default value of {@link #getWarLocation()}.
*/
public static final String DEFAULT_WAR_LOCATION = "reg:.*\\.war";
/**
* The default value of {@link #getServerPort()}.
*/
public static final int DEFAULT_SERVER_PORT = 9090;
/**
* The default value of {@link #getServerPort()} as string.
*/
public static final String DEFAULT_SERVER_PORT_AS_STRING = Integer.toString(DEFAULT_SERVER_PORT);
/**
* The default value of {@link #getContextPath()}.
*/
public static final String DEFAULT_CONTEXT_PATH = "/its";
/**
* The default value of {@link #isStartServer()}.
*/
public static final boolean DEFAULT_START_SERVER = true;
/**
* The default value of {@link #isStartServer()} as string.
*/
public static final String DEFAULT_START_SERVER_AS_STRING = Boolean.toString(DEFAULT_START_SERVER);
/**
* The default value of {@link #isDeployWebapp()}.
*/
public static final boolean DEFAULT_DEPLOY_WEBAPP = true;
/**
* The default value of {@link #isDeployWebapp()} as string.
*/
public static final String DEFAULT_DEPLOY_WEBAPP_AS_STRING = Boolean.toString(DEFAULT_DEPLOY_WEBAPP);
/**
* The default value of {@link #isSkip()}.
*/
public static final boolean DEFAULT_SKIP = false;
/**
* The default value of {@link #isSkip()} as string.
*/
public static final String DEFAULT_SKIP_AS_STRING = Boolean.toString(DEFAULT_SKIP);
/**
* The default value of {@link #getServerLifeCycleListenerClass()}.
*/
public static final Class<? extends ServerLifeCycleListener> DEFAULT_CYCLE_LISTENER_CLASS = NopServerLifeCycleListener.class;
/**
* The default value of {@link #getServerLifeCycleListenerClass()} as string.
*/
public static final String DEFAULT_CYCLE_LISTENER_CLASS_AS_STRING = DEFAULT_CYCLE_LISTENER_CLASS.getName();
private String warLocation = DEFAULT_WAR_LOCATION;
private int serverPort = DEFAULT_SERVER_PORT;
private String contextPath = DEFAULT_CONTEXT_PATH;
private boolean startServer = DEFAULT_START_SERVER;
private boolean deployWebapp = DEFAULT_DEPLOY_WEBAPP;
private boolean skip = DEFAULT_SKIP;
private Class<? extends ServerLifeCycleListener> serverLifeCycleListenerClass = DEFAULT_CYCLE_LISTENER_CLASS;
private Class<?> sourceClass;
private Method sourceMethod;
private JettyRunWar sourceConfig;
}