### Eclipse Workspace Patch 1.0 #P cargo-trunks Index: core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossPropertySet.java =================================================================== --- core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossPropertySet.java (revision 1852) +++ core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossPropertySet.java (working copy) @@ -35,4 +35,12 @@ * Whether the JBoss Configuration should be clustered. */ String CLUSTERED = "cargo.jboss.clustered"; + + /** + * Port number to serve deployable through. Default is 18080. We need to use + * the same port all the time since JBoss relies on the URL to recognise the + * deployable. + */ + String DEPLOYABLE_HOST_PORT = "cargo.jboss.port"; + } Index: core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossInstalledLocalDeployer.java =================================================================== --- core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossInstalledLocalDeployer.java (revision 1852) +++ core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossInstalledLocalDeployer.java (working copy) @@ -117,7 +117,7 @@ } /** - * Checks whether file or dir represented by string exists + * Checks whether file or dir represented by string exists. * * @param fileName path to check * @return true if file/dir exists Index: core/containers/jboss/pom.xml =================================================================== --- core/containers/jboss/pom.xml (revision 1852) +++ core/containers/jboss/pom.xml (working copy) @@ -27,4 +27,18 @@ Cargo Core JBoss Container jar Core API implementation for JBoss containers + + + + org.mortbay.jetty + jetty + 6.1.7 + + + org.mortbay.jetty + jetty-util + 6.1.7 + + + Index: core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossRemoteDeployer.java =================================================================== --- core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossRemoteDeployer.java (revision 1852) +++ core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JBossRemoteDeployer.java (working copy) @@ -26,14 +26,15 @@ import org.codehaus.cargo.container.ContainerException; import org.codehaus.cargo.container.RemoteContainer; +import org.codehaus.cargo.container.configuration.RuntimeConfiguration; +import org.codehaus.cargo.container.deployable.Deployable; import org.codehaus.cargo.container.jboss.internal.HttpURLConnection; import org.codehaus.cargo.container.jboss.internal.JdkHttpURLConnection; -import org.codehaus.cargo.container.configuration.RuntimeConfiguration; -import org.codehaus.cargo.container.deployable.Deployable; import org.codehaus.cargo.container.property.GeneralPropertySet; +import org.codehaus.cargo.container.property.RemotePropertySet; import org.codehaus.cargo.container.property.ServletPropertySet; -import org.codehaus.cargo.container.property.RemotePropertySet; import org.codehaus.cargo.container.spi.deployer.AbstractRemoteDeployer; +import org.codehaus.cargo.util.CargoException; /** * Remote deployer that uses JMX to deploy to JBoss. @@ -56,19 +57,22 @@ * The JBoss JMX deployment URL. */ private String deployURL = "/jmx-console/HtmlAdaptor?action=invokeOpByName&" - + "name=jboss.system:service%3DMainDeployer&methodName=deploy&argType=java.net.URL&arg0="; + + "name=jboss.system:service%3DMainDeployer&methodName=deploy&" + + "argType=java.net.URL&arg0="; /** * The JBoss JMX undeployment URL. */ private String undeployURL = "/jmx-console/HtmlAdaptor?action=invokeOpByName&" - + "name=jboss.system:service%3DMainDeployer&methodName=undeploy&argType=java.net.URL&arg0="; + + "name=jboss.system:service%3DMainDeployer&methodName=undeploy&" + + "argType=java.net.URL&arg0="; /** * The JBoss JMX redeployment URL. */ private String redeployURL = "/jmx-console/HtmlAdaptor?action=invokeOpByName&" - + "name=jboss.system:service%3DMainDeployer&methodName=redeploy&argType=java.net.URL&arg0="; + + "name=jboss.system:service%3DMainDeployer&methodName=redeploy&" + + "argType=java.net.URL&arg0="; /** * The configuration object containing the deployer config data. @@ -133,7 +137,7 @@ */ public void deploy(Deployable deployable) { - invokeURL(createJBossRemoteURL(deployable, this.deployURL)); + invokeRemotely(deployable, this.deployURL, true); } /** @@ -142,7 +146,7 @@ */ public void undeploy(Deployable deployable) { - invokeURL(createJBossRemoteURL(deployable, this.undeployURL)); + invokeRemotely(deployable, this.undeployURL, false); } /** @@ -151,11 +155,61 @@ */ public void redeploy(Deployable deployable) { - invokeURL(createJBossRemoteURL(deployable, this.redeployURL)); + invokeRemotely(deployable, this.redeployURL, true); } - + /** - * @param url the JBoss JMX URL to invoke + * @param deployable + * deployable to deploy + * @param jmxConsoleURL + * URL to jmx console + * @param expectDownload + * expect deployable to be downloaded + */ + private void invokeRemotely(Deployable deployable, String jmxConsoleURL, + boolean expectDownload) + { + FileServer fileServer = setupFileServer(deployable); + try + { + String encodedURL = encodeURLLocation(fileServer.getURL()); + invokeURL(createJBossRemoteURL(deployable, jmxConsoleURL, + encodedURL)); + if (fileServer.getCallCount() == 0 && expectDownload) + { + throw new CargoException( + "Application server didn't request the file"); + } + } + finally + { + fileServer.shutdown(); + } + } + + /** + * @param deployable + * deployable to deploy + * @return file server instance + */ + protected FileServer setupFileServer(Deployable deployable) + { + String portStr = configuration.getPropertyValue(JBossPropertySet.DEPLOYABLE_HOST_PORT); + int port; + if (portStr == null) + { + port = 18080; + } + else + { + port = Integer.parseInt(portStr); + } + return new JettyHTTPFileServer(port, deployable.getFile(), getLogger()); + } + + /** + * @param url + * the JBoss JMX URL to invoke */ private void invokeURL(String url) { @@ -180,21 +234,23 @@ } /** - * @param deployable the deployable for which we'll URL-encode the location + * @param url + * url to encode * @return the URL-encoded location that can be passed in a URL */ - private String encodeDeployableLocation(Deployable deployable) + private String encodeURLLocation(String url) { String encodedString; try { - encodedString = URLEncoder.encode(deployable.getFile(), "UTF-8"); + encodedString = URLEncoder.encode(url, "UTF-8"); } catch (UnsupportedEncodingException e) { - throw new ContainerException("Failed to encode Deployable location [" - + deployable.getFile() + "] using an [UTF-8] encoding", e); + throw new ContainerException( + "Failed to encode Deployable location [" + url + + "] using an [UTF-8] encoding", e); } return encodedString; @@ -202,19 +258,23 @@ /** * Compute the JBoss deploy/undeploy URL. - * - * @param deployable the file to deploy/undeploy - * @param urlPrefix the JBoss static part of the deploy§undeploy URL + * + * @param deployable + * the file to deploy/undeploy + * @param urlPrefix + * the JBoss static part of the deploy§undeploy URL + * @param httpURL + * URL for JBoss to call back on * @return the full deploy/undeploy URL */ - protected String createJBossRemoteURL(Deployable deployable, String urlPrefix) + protected String createJBossRemoteURL(Deployable deployable, + String urlPrefix, String httpURL) { - return this.configuration.getPropertyValue(GeneralPropertySet.PROTOCOL) + "://" - + this.configuration.getPropertyValue(GeneralPropertySet.HOSTNAME) - + ":" - + this.configuration.getPropertyValue(ServletPropertySet.PORT) - + urlPrefix - + "file:" - + encodeDeployableLocation(deployable); + return this.configuration.getPropertyValue(GeneralPropertySet.PROTOCOL) + + "://" + + this.configuration + .getPropertyValue(GeneralPropertySet.HOSTNAME) + ":" + + this.configuration.getPropertyValue(ServletPropertySet.PORT) + + urlPrefix + httpURL; } } Index: core/containers/jboss/src/test/java/org/codehaus/cargo/container/jboss/JettyHTTPFileServerTest.java =================================================================== --- core/containers/jboss/src/test/java/org/codehaus/cargo/container/jboss/JettyHTTPFileServerTest.java (revision 0) +++ core/containers/jboss/src/test/java/org/codehaus/cargo/container/jboss/JettyHTTPFileServerTest.java (revision 0) @@ -0,0 +1,99 @@ +package org.codehaus.cargo.container.jboss; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +import junit.framework.TestCase; + +import org.codehaus.cargo.util.log.NullLogger; + +public class JettyHTTPFileServerTest extends TestCase +{ + + public void test() throws Exception + { + File file = createBinaryTempFile(); + JettyHTTPFileServer fileServer = new JettyHTTPFileServer(0, file + .getPath(), new NullLogger()); + try + { + String urlStr = fileServer.getURL(); + assertTrue(urlStr.indexOf("0.0.0.0") == -1); + assertTrue(urlStr.indexOf("127.0.0.1") == -1); + assertTrue(urlStr.indexOf("localhost") == -1); + URL okURL = new URL(urlStr); + HttpURLConnection okConn = (HttpURLConnection) okURL + .openConnection(); + byte[] bs = readBytes(okConn.getInputStream()); + assertEquals(256, bs.length); + for (int i = 0; i < 256; ++i) + { + int bsi = bs[i]; + if (bsi < 0) + { + bsi += 256; + } + assertEquals(i, bsi); + } + assertEquals(1, fileServer.getCallCount()); + + // Try another file where we should get "401 Unauthorized" + URL failURL = new URL(urlStr + "_NOT"); + HttpURLConnection failConn = (HttpURLConnection) failURL + .openConnection(); + try + { + readBytes(failConn.getInputStream()); + } + catch (IOException e) + { + assertTrue(e.getMessage().indexOf("401") != -1); + } + } + finally + { + fileServer.shutdown(); + + } + } + + private File createBinaryTempFile() throws Exception + { + File tmp = File.createTempFile("test-", "bin"); + FileOutputStream fos = null; + try + { + fos = new FileOutputStream(tmp); + for (int i = 0; i < 256; ++i) + { + fos.write(i); + } + } + finally + { + if (fos != null) + { + fos.close(); + } + } + return tmp; + } + + private byte[] readBytes(InputStream is) throws Exception + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[100]; + int len; + while ((len = is.read(buf)) != -1) + { + baos.write(buf, 0, len); + } + return baos.toByteArray(); + } + +} Index: core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JettyHTTPFileServer.java =================================================================== --- core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JettyHTTPFileServer.java (revision 0) +++ core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/JettyHTTPFileServer.java (revision 0) @@ -0,0 +1,142 @@ +package org.codehaus.cargo.container.jboss; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.codehaus.cargo.util.log.Logger; +import org.mortbay.jetty.Handler; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.handler.HandlerList; +import org.mortbay.jetty.handler.ResourceHandler; + +/** + * File server that serves files over HTTP protocol through a Jetty server. + * + * @version $Id: $ + */ +public class JettyHTTPFileServer implements FileServer +{ + + /** + * Number of calls served. + */ + protected int callCount; + + /** + * Logger instance. + */ + protected Logger logger; + + /** + * URL for retrieving file. + */ + protected String url; + + /** + * Jetty server instance. + */ + protected Server server; + + /** + * @param port + * port number to open (use 0 for random) + * @param fileName + * file to make accessible + * @param logger + * logger instance + */ + public JettyHTTPFileServer(int port, String fileName, Logger logger) + { + this.logger = logger; + final File file = new File(fileName); + if (!file.exists() || !file.isFile()) + { + throw new RuntimeException("File " + fileName + + " does not exist or is not a file"); + } + server = new Server(port); + ResourceHandler resourceHandler = new ResourceHandler() + { + public void handle(String target, HttpServletRequest req, + HttpServletResponse resp, int dispatch) throws IOException, + ServletException + { + if (target.equals("/" + file.getName())) + { + ++callCount; + super.handle(target, req, resp, dispatch); + } + else + { + resp.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } + } + }; + resourceHandler.setResourceBase(file.getParentFile().getPath()); + + HandlerList handlerList = new HandlerList(); + Handler[] handlers = new Handler[] {resourceHandler}; + handlerList.setHandlers(handlers); + server.setHandler(handlerList); + + try + { + server.start(); + url = "http://" + InetAddress.getLocalHost().getCanonicalHostName() + + ":" + server.getConnectors()[0].getLocalPort() + "/" + + file.getName(); + } + catch (UnknownHostException e) + { + throw new RuntimeException("Error retrieving host name", e); + } + catch (Exception e) + { + throw new RuntimeException("Error starting jetty server", e); + } + } + + /** + * {@inheritDoc} + */ + public int getCallCount() + { + return callCount; + } + + /** + * {@inheritDoc} + */ + public String getURL() + { + return url; + } + + /** + * {@inheritDoc} + */ + public void shutdown() + { + try + { + server.stop(); + server.join(); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + return; + } + catch (Exception e) + { + throw new RuntimeException("Error stopping jetty server", e); + } + } + +} Index: core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/FileServer.java =================================================================== --- core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/FileServer.java (revision 0) +++ core/containers/jboss/src/main/java/org/codehaus/cargo/container/jboss/FileServer.java (revision 0) @@ -0,0 +1,26 @@ +package org.codehaus.cargo.container.jboss; + +/** + * Generic file server interface. + * + * @version $Id: JBossRemoteDeployer.java 1198 2006-11-01 13:39:02Z vmassol $ + */ +public interface FileServer +{ + + /** + * @return URL to fetch file from + */ + String getURL(); + + /** + * @return number of requests + */ + int getCallCount(); + + /** + * Shutdown service. + */ + void shutdown(); + +} \ No newline at end of file Index: core/containers/jboss/src/test/java/org/codehaus/cargo/container/jboss/JBossRemoteDeployerTest.java =================================================================== --- core/containers/jboss/src/test/java/org/codehaus/cargo/container/jboss/JBossRemoteDeployerTest.java (revision 1852) +++ core/containers/jboss/src/test/java/org/codehaus/cargo/container/jboss/JBossRemoteDeployerTest.java (working copy) @@ -19,14 +19,12 @@ */ package org.codehaus.cargo.container.jboss; -import org.jmock.MockObjectTestCase; -import org.jmock.Mock; import org.codehaus.cargo.container.RemoteContainer; +import org.codehaus.cargo.container.configuration.RuntimeConfiguration; +import org.codehaus.cargo.container.deployable.Deployable; import org.codehaus.cargo.container.jboss.internal.HttpURLConnection; -import org.codehaus.cargo.container.deployable.Deployable; -import org.codehaus.cargo.container.configuration.RuntimeConfiguration; - -import java.io.File; +import org.jmock.Mock; +import org.jmock.MockObjectTestCase; /** * Unit tests for {@link JBossRemoteDeployer}. @@ -55,7 +53,7 @@ Mock mockDeployable = mock(Deployable.class); mockDeployable.stubs().method("getFile").will( - returnValue("c:/Something With Space/dummy.war")); + returnValue("c:/dummy/Something With Space.war")); Mock mockConnection = mock(HttpURLConnection.class); String expectedURLPortion1 = "http://localhost:8888/"; @@ -64,8 +62,32 @@ .with(and(stringContains(expectedURLPortion1), stringContains(expectedURLPortion2)), eq("john"), eq("doe")); - JBossRemoteDeployer deployer = new JBossRemoteDeployer((RemoteContainer) mockContainer.proxy(), - (HttpURLConnection) mockConnection.proxy()); + JBossRemoteDeployer deployer = new JBossRemoteDeployer( + (RemoteContainer) mockContainer.proxy(), + (HttpURLConnection) mockConnection.proxy()) + { + protected FileServer setupFileServer(Deployable deployable) + { + return new FileServer() + { + public int getCallCount() + { + return 1; + } + + public String getURL() + { + return "http://somewhere/Something With Space.war"; + } + + public void shutdown() + { + // OK + } + }; + } + }; deployer.deploy((Deployable) mockDeployable.proxy()); } + }