Permalink
Browse files

Implementation for servlet protocol.

- Force undeployment before deployment if deployment is required.
- Workaround to AS7-4620.
  • Loading branch information...
1 parent 81ad74b commit d7c81f81fad9b31a1aff6101226537648e8a8d93 @blabno blabno committed with aslakknutsen Apr 25, 2012
View
@@ -1,7 +1,26 @@
JRebel - The Arquillian Experience
-----------------------------------
+==================================
+This extension is for speeding up test development cycle. It deploys the package only once and then leverages [JRebel][jrebel] to hot deploy changed files.
+Usage
+-----
+Just add impl module to classpath and run test either from IDE or maven.
-http://zeroturnaround.com/jrebel/
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-jrebel-impl</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ </dependency>
+
+**Make sure you use servlet protocol!** To do that add following to arquillian.xml:
+
+ <defaultProtocol type="Servlet 3.0"/>
+
+First time you run test it will deploy the package, export the deployment to target/jrebel-temp directory and attach auto generated rebel.xml file that instructs JRebel to override deployed package with the one exported to target/jrebel-temp. Next time you run the test Arquillian will check if package exists in the temp directory and if so it will run tests without deploying the package.
+
+If you want to force Arquillian to deploy the package again to the container you have to delete the temp directory.
+
+
+[jrebel]: http://zeroturnaround.com/jrebel/
View
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <!-- Parent -->
+ <parent>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-jrebel-parent</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ </parent>
+
+ <!-- Model Version -->
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- Artifact Configuration -->
+ <artifactId>arquillian-jrebel-impl</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ <name>Arquillian Extension JRebel Implementation</name>
+ <url>http://www.jboss.org</url>
+ <description>Arquillian Extension JRebel</description>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>arquillian-container-impl-base</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.junit</groupId>
+ <artifactId>arquillian-junit-container</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
@@ -0,0 +1,157 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @authors tag. All rights reserved.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.jboss.arquillian.extension.jrebel;
+
+import org.jboss.arquillian.container.spi.client.deployment.Deployment;
+import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription;
+import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext;
+import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
+import org.jboss.arquillian.container.spi.context.annotation.DeploymentScoped;
+import org.jboss.arquillian.container.spi.event.DeployDeployment;
+import org.jboss.arquillian.container.spi.event.DeploymentEvent;
+import org.jboss.arquillian.container.spi.event.UnDeployDeployment;
+import org.jboss.arquillian.core.api.Event;
+import org.jboss.arquillian.core.api.InstanceProducer;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.core.api.annotation.Observes;
+import org.jboss.arquillian.core.spi.EventContext;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.exporter.ExplodedExporter;
+import org.jboss.shrinkwrap.api.formatter.Formatters;
+
+import java.io.File;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * DeploymentInterceptor
+ *
+ * @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
+ * @version $Revision: $
+ */
+public class DeploymentInterceptor {
+// ------------------------------ FIELDS ------------------------------
+
+ private static final Logger LOGGER = Logger.getLogger(DeploymentInterceptor.class.getName());
+
+ @Inject
+ @DeploymentScoped
+ private InstanceProducer<DeploymentDescription> deploymentDescriptionProducer;
+
+ @Inject
+ @DeploymentScoped
+ private InstanceProducer<Deployment> deploymentProducer;
+
+ @Inject
+ private Event<DeploymentEvent> event;
+
+ private boolean forcedUndeployment;
+
+ @Inject
+ @DeploymentScoped
+ private InstanceProducer<ProtocolMetaData> protocolMetaData;
+
+ private final File tempDirectory;
+
+// --------------------------- CONSTRUCTORS ---------------------------
+
+ public DeploymentInterceptor()
+ {
+ tempDirectory = ShrinkWrapUtil.createTempDirectory();
+ }
+
+// -------------------------- OTHER METHODS --------------------------
+
+ public void onDeploy(@Observes(precedence = -1) EventContext<DeployDeployment> eventContext)
+ {
+ final Deployment deployment = eventContext.getEvent().getDeployment();
+ Archive<?> archive = deployment.getDescription().getTestableArchive();
+ File exportPath = new File(tempDirectory, archive.getName());
+ File metaDataFile = new File(tempDirectory, archive.getName() + ".meta");
+ boolean alreadyDeployed = exportPath.exists() && metaDataFile.exists();
+ //noinspection ResultOfMethodCallIgnored
+ exportPath.mkdirs();
+ archive.as(ExplodedExporter.class).exportExploded(tempDirectory);
+
+
+ if (!alreadyDeployed) {
+ forcedUndeployment = true;
+ try {
+ event.fire(new UnDeployDeployment(eventContext.getEvent().getContainer(), deployment));
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Cannot undeploy " + deployment.getDescription().getName(), e);
+ } finally {
+ forcedUndeployment = false;
+ }
+// TODO if rebel.xml already exists in the archive then don't add new one and don't do exploded export
+ String rebelXml = createRebelXML(exportPath);
+ archive.add(new StringAsset(rebelXml), "WEB-INF/classes/rebel.xml");
+
+ System.out.println(archive.toString(Formatters.VERBOSE));
+ System.out.println();
+ System.out.println(rebelXml);
+
+ eventContext.proceed();
+
+ HTTPContext httpContext = protocolMetaData.get().getContext(HTTPContext.class);
+ if (httpContext != null) {
+ Serializer.toStream(new SerializableHttpContextData(httpContext), metaDataFile);
+ }
+ } else {
+ ProtocolMetaData metaData = new ProtocolMetaData();
+ SerializableHttpContextData serializableHttpContextData = Serializer.toObject(SerializableHttpContextData.class, metaDataFile);
+ metaData.addContext(serializableHttpContextData.toHTTPContext());
+ protocolMetaData.set(metaData);
+ deployment.deployed();
+ deploymentProducer.set(deployment);
+ deploymentDescriptionProducer.set(deployment.getDescription());
+ }
+ }
+
+ public void onUnDeploy(@Observes EventContext<UnDeployDeployment> eventContext)
+ {
+ if (forcedUndeployment) {
+ eventContext.proceed();
+ }
+ }
+
+ private String createRebelXML(File output)
+ {
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<application\n" +
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+ " xmlns=\"http://www.zeroturnaround.com\"\n" +
+ " xsi:schemaLocation=\"http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd\">\n" +
+ " <war dir=\"" + output.getAbsolutePath() + "\" />\n" +
+ /*
+ " <classpath>\n" +
+ " <dir name=\"" + output.getAbsolutePath() + "/WEB-INF/classes\"/>\n" +
+ " </classpath>\n" +
+ " <web>\n" +
+ " <link target=\"/\">\n" +
+ " <dir name=\"" + output.getAbsolutePath() + "/\"/>\n" +
+ " </link>\n" +
+ " <link target=\"/WEB-INF\">\n" +
+ " <dir name=\"" + output.getAbsolutePath() + "/WEB-INF/\"/>\n" +
+ " </link>\n" +
+ " </web>\n" +
+ */
+ "</application>";
+ }
+}
@@ -25,11 +25,15 @@
* @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
* @version $Revision: $
*/
-public class JRebelExtension implements LoadableExtension
-{
- @Override
- public void register(ExtensionBuilder builder)
- {
- builder.observer(DeploymentInterceptor.class);
- }
+public class JRebelExtension implements LoadableExtension {
+// ------------------------ INTERFACE METHODS ------------------------
+
+
+// --------------------- Interface LoadableExtension ---------------------
+
+ @Override
+ public void register(ExtensionBuilder builder)
+ {
+ builder.observer(DeploymentInterceptor.class);
+ }
}
@@ -0,0 +1,70 @@
+package org.jboss.arquillian.extension.jrebel;
+
+import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SerializableHttpContextData implements Serializable {
+// ------------------------------ FIELDS ------------------------------
+
+ private String host;
+
+ private String name;
+
+ private int port;
+
+ private Set<Servlet> servlets;
+
+// --------------------------- CONSTRUCTORS ---------------------------
+
+ public SerializableHttpContextData(HTTPContext context)
+ {
+ name = context.getName();
+ host = context.getHost();
+ port = context.getPort();
+ for (org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet servlet : context.getServlets()) {
+ getServlets().add(new Servlet(servlet.getName(), servlet.getContextRoot()));
+ }
+ }
+
+// --------------------- GETTER / SETTER METHODS ---------------------
+
+ private Set<Servlet> getServlets()
+ {
+ if (servlets == null) {
+ servlets = new HashSet<Servlet>();
+ }
+ return servlets;
+ }
+
+// -------------------------- OTHER METHODS --------------------------
+
+ public HTTPContext toHTTPContext()
+ {
+ final HTTPContext httpContext = new HTTPContext(name, host, port);
+ for (Servlet servlet : getServlets()) {
+ httpContext.add(new org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet(servlet.name, servlet.contextRoot));
+ }
+ return httpContext;
+ }
+
+// -------------------------- INNER CLASSES --------------------------
+
+ private class Servlet implements Serializable {
+// ------------------------------ FIELDS ------------------------------
+
+ private String contextRoot;
+
+ private String name;
+
+// --------------------------- CONSTRUCTORS ---------------------------
+
+ public Servlet(String name, String contextRoot)
+ {
+ this.name = name;
+ this.contextRoot = contextRoot;
+ }
+ }
+}
Oops, something went wrong.

0 comments on commit d7c81f8

Please sign in to comment.