
com.googlecode.jeeunit.resin.EmbeddedResinContainer Maven / Gradle / Ivy
/*
* Copyright 2011 Harald Wellmann
*
* 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.googlecode.jeeunit.resin;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.glassfish.embeddable.archive.ScatteredArchive;
import org.glassfish.embeddable.archive.ScatteredArchive.Type;
import com.caucho.resin.HttpEmbed;
import com.caucho.resin.ResinEmbed;
import com.caucho.resin.WebAppEmbed;
import com.googlecode.jeeunit.impl.ClasspathFilter;
import com.googlecode.jeeunit.impl.Configuration;
import com.googlecode.jeeunit.impl.ConfigurationLoader;
import com.googlecode.jeeunit.spi.ContainerLauncher;
/**
* Singleton implementing the {@link ContainerLauncher} functionality for Embedded Resin. The
* configuration file for the deployed web app is expected in
* {@code src/test/resources/resin-web.xml}.
*
* {@link ResinEmbed} does not let us start the server first and then deploy apps, so we actually
* start the container and deploy the application in {@code autodeploy()}.
*
* For some reason, setting the configuration in the ResinEmbed constructor does not seem to work
* (or maybe something was wrong with my config files). The only way that currently works for me is
* embedding a resin-web.xml config file into the WAR and setting the HTTP port for the server
* programmatically, which is a bit awkward and does not let us define the complete configuration in
* a single file.
*
* For overriding the default port 8080, provide a classpath resource jeeunit.properties, setting
* a property {@code jeeunit.resin.http.port} to the desired value.
*
* @author hwellmann
*
*/
public class EmbeddedResinContainer implements ContainerLauncher {
private static EmbeddedResinContainer instance;
private ResinEmbed resin;
private FileFilter classpathFilter;
private String applicationName;
private String contextRoot;
private File configuration;
private boolean isDeployed;
private List metadataFiles = new ArrayList();
private File tempDir;
private static String[] excludes = { "shrinkwrap-", "resin-", "javaee-", "jsr250-",
"org.eclipse.osgi" };
private Configuration config;
private EmbeddedResinContainer() {
tempDir = createTempDir();
setApplicationName("jeeunit");
setContextRoot("jeeunit");
setClasspathFilter(new ClasspathFilter(excludes));
createDefaultMetadata();
}
private void createDefaultMetadata() {
File webInf = new File("src/main/webapp/WEB-INF");
metadataFiles.add(new File(webInf, "web.xml"));
File beansXml = new File(webInf, "beans.xml");
if (!beansXml.exists()) {
beansXml = new File(tempDir, "beans.xml");
try {
beansXml.createNewFile();
}
catch (IOException exc) {
throw new RuntimeException("cannot create " + beansXml);
}
}
metadataFiles.add(beansXml);
metadataFiles.add(new File("src/test/resources/resin-web.xml"));
}
public static synchronized EmbeddedResinContainer getInstance() {
if (instance == null) {
instance = new EmbeddedResinContainer();
}
return instance;
}
private void addShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
FileUtils.deleteDirectory(tempDir);
}
catch (IOException exc) {
// ignore
}
shutdown();
}
});
}
protected String getApplicationName() {
return applicationName;
}
/**
* Sets the Java EE application name.
*
* @param applicationName
*/
protected void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
protected String getContextRoot() {
return contextRoot;
}
/**
* Sets the context root for the deployed test application.
*
* @param contextRoot
*/
protected void setContextRoot(String contextRoot) {
this.contextRoot = contextRoot;
}
protected File getConfiguration() {
return configuration;
}
/**
* Sets the configuration file for the embedded server.
*
* @param configuration
*/
protected void setConfiguration(File configuration) {
this.configuration = configuration;
}
public void setClasspathFilter(FileFilter classpathFilter) {
this.classpathFilter = classpathFilter;
}
public synchronized void launch() {
if (resin != null) {
return;
}
resin = new ResinEmbed();
config = new ConfigurationLoader().load();
HttpEmbed httpPortDef = new HttpEmbed(config.getHttpPort());
resin.addPort(httpPortDef);
resin.setRootDirectory(new File(tempDir, "serverroot").getAbsolutePath());
/*
* Running under "Run as JUnit test" from Eclipse in a separate process, we do not get
* notified when Eclipse is finished running the test suite. The shutdown hook is just to be
* on the safe side.
*/
addShutdownHook();
}
private File buildWar() throws IOException {
ScatteredArchive sar;
File webResourceDir = new File("src/main/webapp");
if (webResourceDir.exists() && webResourceDir.isDirectory()) {
sar = new ScatteredArchive("jeeunit-autodeploy", Type.WAR, webResourceDir);
}
else {
sar = new ScatteredArchive("jeeunit-autodeploy", Type.WAR);
}
String classpath = System.getProperty("java.class.path");
String[] pathElems = classpath.split(File.pathSeparator);
for (String pathElem : pathElems) {
File file = new File(pathElem);
if (file.exists() && classpathFilter.accept(file)) {
sar.addClassPath(file);
}
}
for (File metadata : metadataFiles) {
if (metadata.exists()) {
sar.addMetadata(metadata);
}
}
URI warUri = sar.toURI();
File war = new File(warUri);
File warFile = new File(tempDir, "jeeunit.war");
copyFile(war, warFile);
return war;
}
private void copyFile(File source, File target) throws IOException {
FileInputStream is = new FileInputStream(source);
FileOutputStream os = new FileOutputStream(target);
IOUtils.copy(is, os);
is.close();
os.close();
}
public void shutdown() {
resin.stop();
resin.destroy();
}
public URI autodeploy() {
if (!isDeployed) {
File war;
try {
war = buildWar();
}
catch (IOException exc) {
throw new RuntimeException(exc);
}
WebAppEmbed webApp = new WebAppEmbed("/" + getContextRoot(),
new File(tempDir, "jeeunit-root").getAbsolutePath());
webApp.setArchivePath(war.getAbsolutePath());
resin.addWebApp(webApp);
resin.start();
isDeployed = true;
}
return getContextRootUri();
}
public URI getContextRootUri() {
try {
return new URI(String.format("http://localhost:%d/%s/", config.getHttpPort(), getContextRoot()));
}
catch (URISyntaxException exc) {
throw new RuntimeException(exc);
}
}
public void addMetadata(File file) {
metadataFiles.add(file);
}
private File createTempDir() {
File tmpRoot = FileUtils.getTempDirectory();
File tmpDir = new File(tmpRoot, UUID.randomUUID().toString());
tmpDir.mkdir();
return tmpDir;
}
}