Fix http deployment
diff --git a/subprojects/s4-core/s4-core.gradle b/subprojects/s4-core/s4-core.gradle
index 0905ce7..e94ea8f 100644
--- a/subprojects/s4-core/s4-core.gradle
+++ b/subprojects/s4-core/s4-core.gradle
@@ -33,6 +33,19 @@
     testCompile libraries.gradle_tooling_api

     testCompile libraries.gradle_wrapper

     testCompile libraries.mockito_core
+}
+
+task testJar(type: Jar) {
+    baseName = "test-${project.archivesBaseName}"
+    from sourceSets.test.output
+}
+
+configurations {
+    tests
+}
+
+artifacts {
+    tests testJar
 }

 

 test {

diff --git a/subprojects/s4-core/src/main/java/org/apache/s4/deploy/HttpS4RFetcher.java b/subprojects/s4-core/src/main/java/org/apache/s4/deploy/HttpS4RFetcher.java
index 1bb4c01..bc611d5 100644
--- a/subprojects/s4-core/src/main/java/org/apache/s4/deploy/HttpS4RFetcher.java
+++ b/subprojects/s4-core/src/main/java/org/apache/s4/deploy/HttpS4RFetcher.java
@@ -103,7 +103,7 @@
                     channelFuture.getCause());
         }
 
-        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString());
+        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getPath());
         request.setHeader(HttpHeaders.Names.HOST, host);
         request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
         request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
diff --git a/subprojects/s4-core/src/test/java/org/apache/s4/deploy/TestAutomaticDeployment.java b/subprojects/s4-core/src/test/java/org/apache/s4/deploy/TestAutomaticDeployment.java
index 1fbd37a..63f94f5 100644
--- a/subprojects/s4-core/src/test/java/org/apache/s4/deploy/TestAutomaticDeployment.java
+++ b/subprojects/s4-core/src/test/java/org/apache/s4/deploy/TestAutomaticDeployment.java
@@ -19,13 +19,9 @@
 package org.apache.s4.deploy;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 import junit.framework.Assert;
@@ -41,11 +37,11 @@
 import org.apache.s4.comm.topology.ZNRecordSerializer;
 import org.apache.s4.fixtures.CommTestUtils;
 import org.apache.s4.fixtures.CoreTestUtils;
+import org.apache.s4.fixtures.S4RHttpServer;
 import org.apache.s4.fixtures.ZkBasedTest;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.server.NIOServerCnxn.Factory;
-import org.jboss.netty.handler.codec.http.HttpHeaders;
 import org.junit.After;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -53,10 +49,6 @@
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Files;
 import com.google.inject.Injector;
-import com.sun.net.httpserver.Headers;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
 
 /**
  * Tests deployment of packaged applications <br>
@@ -71,8 +63,8 @@
     private Factory zookeeperServerConnectionFactory;
     private Process forkedNode;
     private ZkClient zkClient;
-    private HttpServer httpServer;
-    private static File tmpAppsDir;
+    private S4RHttpServer s4rHttpServer;
+    public static File tmpAppsDir;
 
     @BeforeClass
     public static void createS4RFiles() throws Exception {
@@ -101,11 +93,12 @@
 
         final String uri = s4rToDeploy.toURI().toString();
 
-        assertDeployment(uri);
+        assertDeployment(uri, zkClient, true);
 
     }
 
-    private void assertDeployment(final String uri) throws KeeperException, InterruptedException, IOException {
+    public static void assertDeployment(final String uri, ZkClient zkClient, boolean createZkAppNode)
+            throws KeeperException, InterruptedException, IOException {
         CountDownLatch signalAppInitialized = new CountDownLatch(1);
         CountDownLatch signalAppStarted = new CountDownLatch(1);
         CommTestUtils.watchAndSignalCreation(AppConstants.INITIALIZED_ZNODE_1, signalAppInitialized,
@@ -113,9 +106,12 @@
         CommTestUtils.watchAndSignalCreation(AppConstants.INITIALIZED_ZNODE_1, signalAppStarted,
                 CommTestUtils.createZkClient());
 
-        ZNRecord record = new ZNRecord(String.valueOf(System.currentTimeMillis()));
-        record.putSimpleField(DistributedDeploymentManager.S4R_URI, uri);
-        zkClient.create("/s4/clusters/cluster1/app/s4App", record, CreateMode.PERSISTENT);
+        if (createZkAppNode) {
+            // otherwise we need to do that through a separate tool
+            ZNRecord record = new ZNRecord(String.valueOf(System.currentTimeMillis()));
+            record.putSimpleField(DistributedDeploymentManager.S4R_URI, uri);
+            zkClient.create("/s4/clusters/cluster1/app/s4App", record, CreateMode.PERSISTENT);
+        }
 
         Assert.assertTrue(signalAppInitialized.await(20, TimeUnit.SECONDS));
         Assert.assertTrue(signalAppStarted.await(20, TimeUnit.SECONDS));
@@ -154,14 +150,10 @@
                         + "/simple-deployable-app-1-0.0.0-SNAPSHOT.s4r")), Files.newOutputStreamSupplier(s4rToDeploy)) > 0);
 
         // we start a
-        InetSocketAddress addr = new InetSocketAddress(8080);
-        httpServer = HttpServer.create(addr, 0);
+        s4rHttpServer = new S4RHttpServer(8080, tmpDir);
+        s4rHttpServer.start();
 
-        httpServer.createContext("/s4", new MyHandler(tmpDir));
-        httpServer.setExecutor(Executors.newCachedThreadPool());
-        httpServer.start();
-
-        assertDeployment("http://localhost:8080/s4/" + s4rToDeploy.getName());
+        assertDeployment("http://localhost:8080/s4/" + s4rToDeploy.getName(), zkClient, true);
 
         // check resource loading (we use a zkclient without custom serializer)
         ZkClient client2 = new ZkClient("localhost:" + CommTestUtils.ZK_PORT);
@@ -177,6 +169,29 @@
         // 1. start s4 nodes. Check that no app is deployed.
         zkClient = new ZkClient("localhost:" + CommTestUtils.ZK_PORT);
         zkClient.setZkSerializer(new ZNRecordSerializer());
+
+        final CountDownLatch signalNodeReady = new CountDownLatch(1);
+
+        zkClient.subscribeChildChanges("/s4/clusters/cluster1/process", new IZkChildListener() {
+
+            @Override
+            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
+                if (currentChilds.size() == 1) {
+                    signalNodeReady.countDown();
+                }
+
+            }
+        });
+
+        checkNoAppAlreadyDeployed(zkClient);
+
+        forkedNode = CoreTestUtils.forkS4Node(new String[] { "-cluster=cluster1" });
+
+        Assert.assertTrue(signalNodeReady.await(10, TimeUnit.SECONDS));
+
+    }
+
+    public static void checkNoAppAlreadyDeployed(ZkClient zkClient) {
         List<String> processes = zkClient.getChildren("/s4/clusters/cluster1/process");
         Assert.assertTrue(processes.size() == 0);
         final CountDownLatch signalProcessesReady = new CountDownLatch(1);
@@ -191,13 +206,6 @@
 
             }
         });
-
-        forkedNode = CoreTestUtils.forkS4Node(new String[] { "-cluster=cluster1" });
-
-        // TODO synchro with ready state from zk
-        Thread.sleep(10000);
-        // Assert.assertTrue(signalProcessesReady.await(10, TimeUnit.SECONDS));
-
     }
 
     // @Before
@@ -214,8 +222,8 @@
     @After
     public void cleanup() throws Exception {
         CommTestUtils.killS4App(forkedNode);
-        if (httpServer != null) {
-            httpServer.stop(0);
+        if (s4rHttpServer != null) {
+            s4rHttpServer.stop();
         }
     }
 
@@ -224,29 +232,3 @@
         System.out.println("Server is listening on port 8080");
     }
 }
-
-class MyHandler implements HttpHandler {
-
-    File tmpDir;
-
-    public MyHandler(File tmpDir) {
-        this.tmpDir = tmpDir;
-    }
-
-    @Override
-    public void handle(HttpExchange exchange) throws IOException {
-        String requestMethod = exchange.getRequestMethod();
-        if (requestMethod.equalsIgnoreCase("GET")) {
-            String fileName = exchange.getRequestURI().getPath().substring("/s4/".length());
-            Headers responseHeaders = exchange.getResponseHeaders();
-            responseHeaders.set(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.BYTES);
-            exchange.sendResponseHeaders(200, Files.toByteArray(new File(tmpDir, fileName)).length);
-
-            OutputStream responseBody = exchange.getResponseBody();
-
-            ByteStreams.copy(new FileInputStream(new File(tmpDir, fileName)), responseBody);
-
-            responseBody.close();
-        }
-    }
-}
diff --git a/subprojects/s4-core/src/test/java/org/apache/s4/fixtures/S4RHttpServer.java b/subprojects/s4-core/src/test/java/org/apache/s4/fixtures/S4RHttpServer.java
new file mode 100644
index 0000000..432475a
--- /dev/null
+++ b/subprojects/s4-core/src/test/java/org/apache/s4/fixtures/S4RHttpServer.java
@@ -0,0 +1,71 @@
+package org.apache.s4.fixtures;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+
+import org.jboss.netty.handler.codec.http.HttpHeaders;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+public class S4RHttpServer {
+
+    int port;
+    File dir;
+    private HttpServer httpServer;
+
+    public S4RHttpServer(int port, File dir) {
+        this.port = port;
+        this.dir = dir;
+    }
+
+    public void start() throws IOException {
+        InetSocketAddress addr = new InetSocketAddress(8080);
+        httpServer = HttpServer.create(addr, 0);
+
+        httpServer.createContext("/s4", new HttpS4RServingHandler(dir));
+        httpServer.setExecutor(Executors.newCachedThreadPool());
+        httpServer.start();
+    }
+
+    public void stop() {
+        if (httpServer != null) {
+            httpServer.stop(0);
+        }
+    }
+
+    class HttpS4RServingHandler implements HttpHandler {
+
+        File tmpDir;
+
+        public HttpS4RServingHandler(File tmpDir) {
+            this.tmpDir = tmpDir;
+        }
+
+        @Override
+        public void handle(HttpExchange exchange) throws IOException {
+            String requestMethod = exchange.getRequestMethod();
+            if (requestMethod.equalsIgnoreCase("GET")) {
+                String fileName = exchange.getRequestURI().getPath().substring("/s4/".length());
+                Headers responseHeaders = exchange.getResponseHeaders();
+                responseHeaders.set(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.BYTES);
+                exchange.sendResponseHeaders(200, Files.toByteArray(new File(tmpDir, fileName)).length);
+
+                OutputStream responseBody = exchange.getResponseBody();
+
+                ByteStreams.copy(new FileInputStream(new File(tmpDir, fileName)), responseBody);
+
+                responseBody.close();
+            }
+        }
+    }
+
+}
diff --git a/subprojects/s4-tools/s4-tools.gradle b/subprojects/s4-tools/s4-tools.gradle
index f8822df..ef9c8f4 100644
--- a/subprojects/s4-tools/s4-tools.gradle
+++ b/subprojects/s4-tools/s4-tools.gradle
@@ -38,6 +38,9 @@
     compile libraries.gradle_tooling_api

     compile libraries.gradle_wrapper
     compile libraries.log4j

+    testCompile project(path: ':s4-comm', configuration: 'tests')
+    testCompile project(path: ':s4-core', configuration: 'tests')
+    
 

 }

 

diff --git a/subprojects/s4-tools/src/main/java/org/apache/s4/tools/Deploy.java b/subprojects/s4-tools/src/main/java/org/apache/s4/tools/Deploy.java
index 94e359c..ac8e214 100644
--- a/subprojects/s4-tools/src/main/java/org/apache/s4/tools/Deploy.java
+++ b/subprojects/s4-tools/src/main/java/org/apache/s4/tools/Deploy.java
@@ -19,6 +19,7 @@
 package org.apache.s4.tools;
 
 import java.io.File;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -66,18 +67,17 @@
                 System.exit(1);
             }
 
-            File s4rToDeploy;
+            URI s4rURI;
 
             if (deployArgs.s4rPath != null) {
-                s4rToDeploy = new File(deployArgs.s4rPath);
-                if (!s4rToDeploy.exists()) {
-                    logger.error("Specified S4R file does not exist in {}", s4rToDeploy.getAbsolutePath());
-                    System.exit(1);
-                } else {
-                    logger.info(
-                            "Using specified S4R [{}], the S4R archive will not be built from source (and corresponding parameters are ignored)",
-                            s4rToDeploy.getAbsolutePath());
-                }
+                s4rURI = new URI(deployArgs.s4rPath);
+                // if (!s4rToDeploy.exists()) {
+                // logger.error("Specified S4R file does not exist in {}", s4rToDeploy.getAbsolutePath());
+                // System.exit(1);
+                // } else {
+                logger.info(
+                        "Using specified S4R [{}], the S4R archive will not be built from source (and corresponding parameters are ignored)",
+                        s4rURI.toString());
             } else {
                 List<String> params = new ArrayList<String>();
                 // prepare gradle -P parameters, including passed gradle opts
@@ -89,21 +89,20 @@
                 File tmpS4R = new File(tmpAppsDir.getAbsolutePath() + "/" + deployArgs.appName + ".s4r");
                 if (!Strings.isNullOrEmpty(deployArgs.generatedS4R)) {
                     logger.info("Copying generated S4R to [{}]", deployArgs.generatedS4R);
-                    s4rToDeploy = new File(deployArgs.generatedS4R);
+                    s4rURI = new URI(deployArgs.generatedS4R);
                     if (!(ByteStreams.copy(Files.newInputStreamSupplier(tmpS4R),
-                            Files.newOutputStreamSupplier(s4rToDeploy)) > 0)) {
+                            Files.newOutputStreamSupplier(new File(s4rURI))) > 0)) {
                         logger.error("Cannot copy generated s4r from {} to {}", tmpS4R.getAbsolutePath(),
-                                s4rToDeploy.getAbsolutePath());
+                                s4rURI.toString());
                         System.exit(1);
                     }
                 } else {
-                    s4rToDeploy = tmpS4R;
+                    s4rURI = tmpS4R.toURI();
                 }
             }
 
-            final String uri = s4rToDeploy.toURI().toString();
             ZNRecord record = new ZNRecord(String.valueOf(System.currentTimeMillis()));
-            record.putSimpleField(DistributedDeploymentManager.S4R_URI, uri);
+            record.putSimpleField(DistributedDeploymentManager.S4R_URI, s4rURI.toString());
             record.putSimpleField("name", deployArgs.appName);
             String deployedAppPath = "/s4/clusters/" + deployArgs.clusterName + "/app/s4App";
             if (zkClient.exists(deployedAppPath)) {
@@ -117,11 +116,12 @@
             logger.info(
                     "uploaded application [{}] to cluster [{}], using zookeeper znode [{}], and s4r file [{}]",
                     new String[] { deployArgs.appName, deployArgs.clusterName,
-                            "/s4/clusters/" + deployArgs.clusterName + "/app/" + deployArgs.appName,
-                            s4rToDeploy.getAbsolutePath() });
+                            "/s4/clusters/" + deployArgs.clusterName + "/app/" + deployArgs.appName, s4rURI.toString() });
 
             // Explicitly shutdown the JVM since Gradle leaves non-daemon threads running that delay the termination
-            System.exit(0);
+            if (!deployArgs.testMode) {
+                System.exit(0);
+            }
         } catch (Exception e) {
             LoggerFactory.getLogger(Deploy.class).error("Cannot deploy app", e);
         }
@@ -155,6 +155,9 @@
         @Parameter(names = "-timeout", description = "Connection timeout to Zookeeper, in ms")
         int timeout = 10000;
 
+        @Parameter(names = "-testMode", description = "Special mode for regression testing", hidden = true)
+        boolean testMode = false;
+
     }
 
     static class ExecGradle {
diff --git a/subprojects/s4-tools/src/test/java/org/apache/s4/tools/TestDeploy.java b/subprojects/s4-tools/src/test/java/org/apache/s4/tools/TestDeploy.java
new file mode 100644
index 0000000..b9f3b5d
--- /dev/null
+++ b/subprojects/s4-tools/src/test/java/org/apache/s4/tools/TestDeploy.java
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.s4.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+import org.I0Itec.zkclient.IZkChildListener;
+import org.I0Itec.zkclient.ZkClient;
+import org.apache.s4.comm.topology.ZNRecordSerializer;
+import org.apache.s4.deploy.AppConstants;
+import org.apache.s4.deploy.TestAutomaticDeployment;
+import org.apache.s4.fixtures.CommTestUtils;
+import org.apache.s4.fixtures.CoreTestUtils;
+import org.apache.s4.fixtures.S4RHttpServer;
+import org.apache.s4.fixtures.ZkBasedTest;
+import org.junit.After;
+import org.junit.Test;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+
+public class TestDeploy extends ZkBasedTest {
+
+    private Process forkedNode;
+    private S4RHttpServer s4rHttpServer;
+
+    @Test
+    public void testDeployment() throws Exception {
+        TestAutomaticDeployment.createS4RFiles();
+
+        ZkClient zkClient = new ZkClient("localhost:" + CommTestUtils.ZK_PORT);
+        zkClient.setZkSerializer(new ZNRecordSerializer());
+
+        final CountDownLatch signalNodeReady = new CountDownLatch(1);
+
+        zkClient.subscribeChildChanges("/s4/clusters/cluster1/process", new IZkChildListener() {
+
+            @Override
+            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
+                if (currentChilds.size() == 1) {
+                    signalNodeReady.countDown();
+                }
+
+            }
+        });
+
+        TestAutomaticDeployment.checkNoAppAlreadyDeployed(zkClient);
+
+        forkedNode = CoreTestUtils.forkS4Node(new String[] { "-cluster=cluster1" });
+
+        Assert.assertTrue(signalNodeReady.await(10, TimeUnit.SECONDS));
+
+        // deploy app
+
+        Assert.assertFalse(zkClient.exists(AppConstants.INITIALIZED_ZNODE_1));
+
+        File tmpDir = Files.createTempDir();
+
+        File s4rToDeploy = new File(tmpDir, String.valueOf(System.currentTimeMillis()));
+
+        Assert.assertTrue(ByteStreams.copy(
+                Files.newInputStreamSupplier(new File(TestAutomaticDeployment.tmpAppsDir.getAbsolutePath()
+                        + "/simple-deployable-app-1-0.0.0-SNAPSHOT.s4r")), Files.newOutputStreamSupplier(s4rToDeploy)) > 0);
+
+        s4rHttpServer = new S4RHttpServer(8080, tmpDir);
+        s4rHttpServer.start();
+
+        // Deploy
+        Deploy.main(new String[] { "-s4r", "http://localhost:8080/s4/" + s4rToDeploy.getName(), "-c", "cluster1",
+                "-appName", "toto", "-testMode" });
+
+        TestAutomaticDeployment.assertDeployment("http://localhost:8080/s4/" + s4rToDeploy.getName(), zkClient, false);
+
+        // check resource loading (we use a zkclient without custom serializer)
+        ZkClient client2 = new ZkClient("localhost:" + CommTestUtils.ZK_PORT);
+        Assert.assertEquals("Salut!", client2.readData("/resourceData"));
+
+    }
+
+    @After
+    public void cleanup() throws IOException, InterruptedException {
+        CoreTestUtils.killS4App(forkedNode);
+        if (s4rHttpServer != null) {
+            s4rHttpServer.stop();
+        }
+    }
+}