common-smartgears/src/test/java/app/SomeApp.java

439 lines
11 KiB
Java

package app;
import static org.gcube.smartgears.Constants.configuration_file_path;
import static org.gcube.smartgears.Constants.extensions_file_path;
import static org.gcube.smartgears.Constants.ghn_home_property;
import static org.gcube.smartgears.Constants.handlers_file_path;
import static utils.TestUtils.context_root;
import static utils.TestUtils.context_root_path;
import static utils.TestUtils.location;
import static utils.TestUtils.servlet_name;
import java.io.File;
import java.io.InputStream;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.io.FileUtils;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationExtensions;
import org.gcube.smartgears.configuration.application.ApplicationHandlers;
import org.gcube.smartgears.configuration.application.DefaultApplicationConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.managers.ContainerManager;
import org.gcube.smartgears.provider.ProviderFactory;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.impl.base.path.BasicPath;
import com.sun.jersey.api.client.ClientResponse;
import utils.TestProvider;
import utils.TestUtils;
/**
* Simulates a single-servlet application to be transformed into a gCube resource.
* <p>
* <ul>
* <li>uses a default configuration that can be customised (cf. {@link #configuration()});
* <li>can be configured with the default handlers (cf. {@link #useDefaultHandlers()}) or custom ones (
* {@link #handlers()}), including those that are not deployable through standard means, such as mocks (
* {@link #bypassHandlerDeployment()};
* <li>can be started to have a default behaviour when called, ({@link #start()}) or else a custom behaviour (
* {@link #startWith(Runnable)};
* </ul>
* can be called in a default scope ({@link #call()} or in a specific scope ({@link #callIn(String)}). Calls are blocking but always
* configured and executed in a separate thread;
*
* @author Fabio Simeoni
*
*/
public class SomeApp {
private final Tomcat tomcat = new Tomcat();
private WebArchive war = defaultWar();
private ContainerConfiguration containerConfiguration;
private ApplicationConfiguration configuration;
private ApplicationHandlers handlers = new ApplicationHandlers();
private ApplicationExtensions extensions = new ApplicationExtensions();
private TestProvider provider = new TestProvider(new File("src/test/resources/test-configuration.ini"));
private boolean deployHandlers = true;
private boolean deployExtensions = true;
private boolean deployConfiguration = true;
private boolean clean=true;
public SomeApp() {
if (ContainerManager.instance!=null)
ContainerManager.instance.stop(true);
tomcat.getConnector().setPort(0);
tomcat.setBaseDir(location);
System.setProperty(ghn_home_property,location);
containerConfiguration = defaultContainerConfiguration();
configuration = defaultConfiguration();
}
/**
* Sets a {@link TestProvider} to resolve dependencies at runtime.
*
* @param provider the provider
*/
public void set(TestProvider provider) {
this.provider = provider;
}
/**
* Runs the test with the state left by previous run.
* <p>
* Use only within a single test!
*/
public void dirtyRun() {
this.clean = false;
}
/**
* Returns the configuration of the application.
* <p>
* The initial configuration is based on defaults, but can be changed and extended.
*
* @return the configuration
*/
public ApplicationConfiguration configuration() {
return configuration;
}
/**
* Returns the configuration of the containerConfiguration.
* <p>
* The initial configuration is based on defaults, but can be changed and extended.
*
* @return the configuration
*/
public ContainerConfiguration containerConfiguration() {
return containerConfiguration;
}
/**
* Returns the handlers that manage the application, none by default.
*
* @return the handlers
*/
public ApplicationHandlers handlers() {
return handlers;
}
/**
* Avoids deployment of the configured handlers in the application's WAR.
* <p>
* The handlers will instead be directly available at runtime.
*/
public void bypassHandlerDeployment() {
provider.use(handlers());
deployHandlers = false;
}
/**
* Avoids resource configuration deployment.
*/
public void asExternal() {
configuration.context(context_root_path);
containerConfiguration.app(configuration);
bypassConfigurationDeployment();
bypassExtensionsDeployment();
bypassHandlerDeployment();
}
/**
* Avoids resource configuration deployment.
*/
public void withExternal(ApplicationConfiguration config) {
config.context(context_root_path);
containerConfiguration.app(config.context()).merge(config);
}
public void bypassConfigurationDeployment() {
deployConfiguration = false;
}
public void usePublisher(ScopedPublisher publisher) {
provider.use(publisher);
}
/**
* Uses default handlers.
*/
public void useDefaultHandlers() {
deployHandlers = false;
}
/**
* Returns the extensions of the application, none by default.
*
* @return the handlers
*/
public ApplicationExtensions extensions() {
return extensions;
}
/**
* Uses default extensions.
*/
public void useDefaultExtensions() {
deployExtensions = false;
}
/**
* Avoids deployment of the configured extensions in the application's WAR.
* <p>
* The extensions will instead be directly available at runtime.
*/
public void bypassExtensionsDeployment() {
provider.use(extensions());
deployExtensions = false;
}
/**
* Starts the application.
*
* @return the context of the application
*/
public ApplicationContext start() {
return startWith(new Runnable() {
@Override
public void run() {
System.err.println("test servlet invoked with no particular task");
}
});
}
/**
* Starts the application, injecting test logic in its single servlet
*
* @param test test logic
* @return the context of the application
*/
public ApplicationContext startWith(Runnable test) {
// install provider
ProviderFactory.testProvider(provider);
if (clean)
cleanupInstallation();
if (deployConfiguration)
deployConfiguration();
if (deployHandlers)
deployHandlers();
if (deployExtensions)
deployExtensions();
try {
System.err.println("deploying with " + war.toString(true));
StandardContext ctx = (StandardContext) tomcat.addWebapp(context_root_path, warFile().getAbsolutePath());
// tells tomcat to look also for exploded directories such as this project's (finds initializer)
((StandardJarScanner) ctx.getJarScanner()).setScanAllDirectories(true);
//this starts webapp and always comes back
tomcat.start();
ApplicationContext context = provider.context;
if (context==null)
throw new RuntimeException("app failed @ startup");
Wrapper webapp = (Wrapper) tomcat.getHost().findChild(context_root_path).findChild(servlet_name);
if (webapp!=null) {
webapp.setServlet(new TestServlet(test));
//context.container().configuration().port(port());
containerConfiguration = context.container().configuration();
}
return context;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Retuens <code>true</code> if the application was successfully start in the container.
*
* @return <code>true</code> if the application was successfully start in the container
*/
public boolean isActive() {
return tomcat.getHost().findChild("/" + context_root).findChild(servlet_name) != null;
}
/**
* Makes a request to the application.
*/
public String send(Request call) {
return call.make(port()).getEntity(String.class);
}
/**
* Makes a request to the application.
*/
public ClientResponse httpSend(Request call) {
return call.make(port());
}
/**
* Stops the application
*/
public void stop() {
try {
tomcat.stop();
tomcat.destroy();
} catch (Exception e) {
System.err.println("WARNING: could not clearly stop container:");
e.printStackTrace();
}
}
public File containerConfigurationFile() {
return new File(location,Constants.container_configuraton_file_path);
}
// helpers
/**
* Includes the configuration in the application's WAR.
*/
private void deployConfiguration() {
String xml = TestUtils.bind(configuration());
System.err.println("deploying with configuration:\n" + xml);
war.addAsWebResource(new StringAsset(xml), new BasicPath(configuration_file_path));
}
/**
* Includes the handlers in the application's WAR.
*/
private void deployHandlers() {
String xml = TestUtils.bind(handlers());
System.err.println("deploying with handlers:\n" + xml);
war.addAsWebResource(new StringAsset(xml), new BasicPath(handlers_file_path));
}
/**
* Includes the extensions in the application's WAR.
*/
private void deployExtensions() {
String xml = TestUtils.bind(extensions());
System.err.println("deploying with extensions:\n" + xml);
war.addAsWebResource(new StringAsset(xml), new BasicPath(extensions_file_path));
}
private File warFile() {
File warFile = new File(location, "test.war");
if (warFile.exists() && !warFile.delete())
System.out.println("could not delete old deployment");; // seems safer than plain overwrite to avoid war corruption
war.as(ZipExporter.class).exportTo(warFile, true);
return warFile;
}
private WebArchive defaultWar() {
WebArchive war = ShrinkWrap.create(WebArchive.class);
war.setWebXML(new File("src/test/java/app/web.xml"));
return war;
}
private ApplicationConfiguration defaultConfiguration() {
return new DefaultApplicationConfiguration().serviceClass("test-class").name("test-app").version("1.0");
}
private ContainerConfiguration defaultContainerConfiguration() {
InputStream is = SomeApp.class.getResourceAsStream("/test-configuration.ini");
return ContainerConfiguration.load(is);
}
public int port() {
return tomcat.getConnector().getLocalPort();
}
private void cleanupInstallation() {
System.out.println("cleaning installation in location "+location);
File installation = new File(location);
if (installation.exists())
try {
FileUtils.deleteDirectory(installation);
}
catch(Exception e) {
throw new RuntimeException(e);
}
installation.mkdirs();
}
}