ace-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From br...@apache.org
Subject svn commit: r1513878 [4/5] - in /ace/trunk: cnf/localrepo/ cnf/localrepo/org.eclipse.jetty.continuation/ cnf/localrepo/org.eclipse.jetty.http/ cnf/localrepo/org.eclipse.jetty.io/ cnf/localrepo/org.eclipse.jetty.jmx/ cnf/localrepo/org.eclipse.jetty.secu...
Date Wed, 14 Aug 2013 13:37:48 GMT
Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ConnectionHandlerImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ConnectionHandlerImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ConnectionHandlerImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ConnectionHandlerImpl.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,233 @@
+/*
+ * 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.ace.agent.impl;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.ace.agent.ConnectionHandler;
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * Default connection handler with support for BASIC authentication and HTTPS client certificates.
+ * 
+ */
+public class ConnectionHandlerImpl implements ConnectionHandler {
+
+    public static final String PROP_AUTHTYPE = "agent.authType";
+    public static final String PROP_AUTHUSER = "agent.authUser";
+    public static final String PROP_AUTHPASS = "agent.authPass";
+    public static final String PROP_AUTHKEYFILE = "agent.authKeyFile";
+    public static final String PROP_AUTHKEYPASS = "agent.authKeyPass";
+    public static final String PROP_AUTHTRUSTFILE = "agent.authTrustFile";
+    public static final String PROP_AUTHTRUSTPASS = "agent.authTrustPass";
+
+    private static final String HTTP_HEADER_AUTHORIZATION = "Authorization";
+
+    private enum AuthType {
+
+        NONE,
+        BASIC,
+        CLIENT_CERT;
+
+        static AuthType getAuthType(String name) {
+            if (name.equals(NONE.name()))
+                return NONE;
+            if (name.equals(BASIC.name()))
+                return BASIC;
+            if (name.equals(CLIENT_CERT.name()))
+                return CLIENT_CERT;
+            return null;
+        }
+    }
+
+    private static class UrlCredentials {
+
+        final static UrlCredentials EMPTY_CREDENTIALS = new UrlCredentials(AuthType.NONE, new Object[0]);
+
+        private final AuthType m_type;
+        private final Object[] m_credentials;
+
+        public UrlCredentials(AuthType type, Object... credentials) {
+            m_type = type;
+            m_credentials = (credentials == null) ? new Object[0] : credentials.clone();
+        }
+
+        public Object[] getCredentials() {
+            return m_credentials.clone();
+        }
+
+        public AuthType getType() {
+            return m_type;
+        }
+    }
+
+    private final AgentContext m_agentContext;
+
+    public ConnectionHandlerImpl(AgentContext agentContext) {
+        m_agentContext = agentContext;
+    }
+
+    @Override
+    public URLConnection getConnection(URL url) throws IOException {
+        URLConnection connection = (HttpURLConnection) url.openConnection();
+        UrlCredentials credentials = getCredentials();
+        if (credentials != null) {
+            if (credentials != null && credentials.getType() == AuthType.BASIC)
+                applyBasicAuthentication(connection, credentials.getCredentials());
+
+            else if (credentials != null && credentials.getType() == AuthType.CLIENT_CERT) {
+                applyClientCertificate(connection, credentials.getCredentials());
+            }
+        }
+        return connection;
+    }
+
+    private final String getBasicAuthCredentials(Object[] values) {
+        if ((values == null) || values.length < 2) {
+            throw new IllegalArgumentException("Insufficient credentials passed: expected 2 values!");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        if (values[0] instanceof String) {
+            sb.append((String) values[0]);
+        }
+        else if (values[0] instanceof byte[]) {
+            sb.append(new String((byte[]) values[0]));
+        }
+        sb.append(':');
+        if (values[1] instanceof String) {
+            sb.append((String) values[1]);
+        }
+        else if (values[1] instanceof byte[]) {
+            sb.append(new String((byte[]) values[1]));
+        }
+
+        return "Basic " + new String(Base64.encodeBase64(sb.toString().getBytes()));
+    }
+
+    private void applyBasicAuthentication(URLConnection conn, Object[] values) {
+        if (conn instanceof HttpURLConnection) {
+            conn.setRequestProperty(HTTP_HEADER_AUTHORIZATION, getBasicAuthCredentials(values));
+        }
+    }
+
+    private void applyClientCertificate(URLConnection conn, Object[] values) {
+        if (conn instanceof HttpsURLConnection) {
+            ((HttpsURLConnection) conn).setSSLSocketFactory(((SSLContext) values[0]).getSocketFactory());
+        }
+    }
+
+    public UrlCredentials getCredentials() {
+
+        String configValue = getConfigurationValue(PROP_AUTHTYPE);
+        AuthType authType = AuthType.getAuthType(configValue == null ? "" : configValue.trim().toUpperCase());
+        if (authType == null || authType == AuthType.NONE) {
+            return UrlCredentials.EMPTY_CREDENTIALS;
+        }
+
+        if (authType == AuthType.BASIC) {
+            String username = getConfigurationValue(PROP_AUTHUSER);
+            String password = getConfigurationValue(PROP_AUTHPASS);
+            return new UrlCredentials(AuthType.BASIC,
+                new Object[] { username == null ? "" : username, password == null ? "" : password });
+        }
+
+        if (authType == AuthType.CLIENT_CERT) {
+            String keystoreFile = getConfigurationValue(PROP_AUTHKEYFILE);
+            String keystorePass = getConfigurationValue(PROP_AUTHKEYPASS);
+            String truststoreFile = getConfigurationValue(PROP_AUTHTRUSTFILE);
+            String truststorePass = getConfigurationValue(PROP_AUTHTRUSTPASS);
+
+            // TODO This is expensive. Can we cache?
+            try {
+                KeyManager[] keyManagers = getKeyManagerFactory(keystoreFile, keystorePass);
+                TrustManager[] trustManagers = getTrustManagerFactory(truststoreFile, truststorePass);
+                SSLContext context = SSLContext.getInstance("TLS");
+                context.init(keyManagers, trustManagers, new SecureRandom());
+                return new UrlCredentials(AuthType.CLIENT_CERT,
+                    new Object[] { context });
+            }
+            catch (Exception e) {
+                // TODO log
+            }
+        }
+        return null;
+    }
+
+    private String getConfigurationValue(String key) {
+        return m_agentContext.getConfigurationHandler().getMap().get(key);
+    }
+
+    private static KeyManager[] getKeyManagerFactory(String keystoreFile, String storePass) throws IOException, GeneralSecurityException {
+        InputStream keyInput = null;
+        try {
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            keyInput = new FileInputStream(keystoreFile);
+            keyStore.load(keyInput, storePass.toCharArray());
+            keyManagerFactory.init(keyStore, storePass.toCharArray());
+            return keyManagerFactory.getKeyManagers();
+        }
+        finally {
+            try {
+                if (keyInput != null)
+                    keyInput.close();
+            }
+            catch (IOException e) {
+                // TODO log
+            }
+        }
+    }
+
+    private static TrustManager[] getTrustManagerFactory(String truststoreFile, String storePass) throws IOException, GeneralSecurityException {
+        InputStream trustInput = null;
+        try {
+            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            trustInput = new FileInputStream(truststoreFile);
+            trustStore.load(trustInput, storePass.toCharArray());
+            trustManagerFactory.init(trustStore);
+            return trustManagerFactory.getTrustManagers();
+        }
+        finally {
+            try {
+                if (trustInput != null)
+                    trustInput.close();
+            }
+            catch (IOException e) {
+                // TODO log
+            }
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,118 @@
+/*
+ * 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.ace.agent.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.SortedSet;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.ace.agent.AgentControl;
+import org.apache.ace.agent.ConfigurationHandler;
+import org.apache.ace.agent.DeploymentHandler;
+import org.apache.ace.agent.RetryAfterException;
+import org.osgi.framework.Version;
+
+/**
+ * Default configurable controller
+ * 
+ */
+public class DefaultController implements Runnable {
+
+    private final AgentControl m_agentControl;
+    private final ScheduledExecutorService m_executorService;
+    private volatile ScheduledFuture<?> m_future;
+
+    public DefaultController(AgentControl agentControl, ScheduledExecutorService executorService) {
+        m_agentControl = agentControl;
+        m_executorService = executorService;
+    }
+
+    public void start() {
+        reSchedule(getSyncInterval());
+    }
+
+    public void stop() {
+        unSchedule();
+    }
+
+    public void run() {
+
+        long syncInterval = getSyncInterval();
+        try {
+            runSafe();
+        }
+        catch (RetryAfterException e) {
+            syncInterval = e.getSeconds();
+        }
+        catch (IOException e) {
+            // TODO what to do
+            e.printStackTrace();
+        }
+        catch (Exception e) {
+            // TODO what to do
+            e.printStackTrace();
+        }
+        reSchedule(syncInterval);
+    }
+
+    public void runSafe() throws RetryAfterException, IOException {
+
+        DeploymentHandler deploymentHandler = getDeploymentHandler();
+
+        Version current = deploymentHandler.getInstalledVersion();
+        SortedSet<Version> available = deploymentHandler.getAvailableVersions();
+        Version highest = Version.emptyVersion;
+        if (available != null && !available.isEmpty())
+            highest = available.last();
+
+        if (highest.compareTo(current) > 1) {
+            InputStream inputStream = deploymentHandler.getInputStream(highest, true);
+            try {
+                deploymentHandler.deployPackage(inputStream);
+            }
+            finally {
+                inputStream.close();
+            }
+        }
+    }
+
+    private void reSchedule(long seconds) {
+        m_future = m_executorService.schedule(this, seconds, TimeUnit.SECONDS);
+    }
+
+    private void unSchedule() {
+        if (m_future != null)
+            m_future.cancel(true);
+    }
+
+    private long getSyncInterval() {
+        return getConfiguration().getSyncInterval();
+    }
+
+    private DeploymentHandler getDeploymentHandler() {
+        return m_agentControl.getDeploymentHandler();
+    }
+
+    private ConfigurationHandler getConfiguration() {
+        return m_agentControl.getConfiguration();
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DeploymentHandlerImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DeploymentHandlerImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DeploymentHandlerImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DeploymentHandlerImpl.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,229 @@
+/*
+ * 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.ace.agent.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.ace.agent.AgentConstants;
+import org.apache.ace.agent.DeploymentHandler;
+import org.apache.ace.agent.DownloadHandle;
+import org.apache.ace.agent.RetryAfterException;
+import org.apache.felix.deploymentadmin.DeploymentAdminImpl;
+import org.osgi.framework.Version;
+import org.osgi.service.deploymentadmin.DeploymentAdmin;
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+
+public class DeploymentHandlerImpl implements DeploymentHandler {
+
+    private final AgentContext m_agentContext;
+    private DeploymentAdmin m_deploymentAdmin;
+
+    public DeploymentHandlerImpl(AgentContext agentContext) {
+        this(agentContext, new DeploymentAdminImpl());
+    }
+
+    public DeploymentHandlerImpl(AgentContext agentContext, DeploymentAdmin deploymentAdmin) {
+        m_agentContext = agentContext;
+        m_deploymentAdmin = deploymentAdmin;
+    }
+
+    @Override
+    public Version getInstalledVersion() {
+        Version highestVersion = Version.emptyVersion;
+        DeploymentPackage[] installedPackages = m_deploymentAdmin.listDeploymentPackages();
+        for (DeploymentPackage installedPackage : installedPackages) {
+            if (installedPackage.getName().equals(getIdentification())
+                && installedPackage.getVersion().compareTo(highestVersion) > 0) {
+                highestVersion = installedPackage.getVersion();
+            }
+        }
+        return highestVersion;
+    }
+
+    @Override
+    public SortedSet<Version> getAvailableVersions() throws RetryAfterException, IOException {
+
+        SortedSet<Version> versions = new TreeSet<Version>();
+
+        URL endpoint = getEndpoint(getServerURL(), getIdentification());
+        URLConnection connection = null;
+        BufferedReader reader = null;
+        try {
+            connection = getConnection(endpoint);
+
+            // TODO handle problems and retries
+            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+            String versionString;
+            while ((versionString = reader.readLine()) != null) {
+                try {
+                    Version version = Version.parseVersion(versionString);
+                    versions.add(version);
+                }
+                catch (IllegalArgumentException e) {
+                    throw new IOException(e);
+                }
+            }
+            return versions;
+        }
+        finally {
+            if (connection != null && connection instanceof HttpURLConnection)
+                ((HttpURLConnection) connection).disconnect();
+            if (reader != null)
+                reader.close();
+        }
+    }
+
+    @Override
+    public long getPackageSize(Version version, boolean fixPackage) throws RetryAfterException, IOException {
+
+        URL url = getPackageURL(version, fixPackage);
+        long packageSize = -1l;
+
+        URLConnection urlConnection = null;
+        InputStream inputStream = null;
+        try {
+            urlConnection = url.openConnection();
+            if (urlConnection instanceof HttpURLConnection)
+                ((HttpURLConnection) urlConnection).setRequestMethod("HEAD");
+
+            String dpSizeHeader = urlConnection.getHeaderField(AgentConstants.HEADER_DPSIZE);
+            if (dpSizeHeader != null)
+                try {
+                    packageSize = Long.parseLong(dpSizeHeader);
+                }
+                catch (NumberFormatException e) {
+                    // ignore
+                }
+            return packageSize;
+        }
+        finally {
+            if (urlConnection != null && urlConnection instanceof HttpURLConnection)
+                ((HttpURLConnection) urlConnection).disconnect();
+            if (inputStream != null)
+                try {
+                    inputStream.close();
+                }
+                catch (IOException e) {
+                    // ignore
+                }
+        }
+    }
+
+    @Override
+    public InputStream getInputStream(Version version, boolean fixPackage) throws RetryAfterException, IOException {
+        URL packageURL = getPackageURL(version, fixPackage);
+        URLConnection urlConnection = null;
+        InputStream inputStream = null;
+        try {
+            // TODO handle problems and retries
+            urlConnection = packageURL.openConnection();
+            inputStream = urlConnection.getInputStream();
+            return inputStream;
+        }
+        finally {
+            if (urlConnection != null && urlConnection instanceof HttpURLConnection)
+                ((HttpURLConnection) urlConnection).disconnect();
+            if (inputStream != null)
+                try {
+                    inputStream.close();
+                }
+                catch (IOException e) {
+                    e.printStackTrace();
+                }
+        }
+    }
+
+    @Override
+    public DownloadHandle getDownloadHandle(Version version, boolean fixPackage) {
+        URL packageURL = getPackageURL(version, fixPackage);
+        DownloadHandle downloadHandle = m_agentContext.getDownloadHandler().getHandle(packageURL);
+        return downloadHandle;
+    }
+
+    @Override
+    public void deployPackage(InputStream inputStream) {
+        // FIXME exceptions
+        try {
+            m_deploymentAdmin.installDeploymentPackage(inputStream);
+        }
+        catch (DeploymentException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private URL getPackageURL(Version version, boolean fixPackage) {
+        URL url = null;
+        if (fixPackage) {
+            url = getEndpoint(getServerURL(), getIdentification(), getInstalledVersion(), version);
+        }
+        else {
+            url = getEndpoint(getServerURL(), getIdentification(), version);
+        }
+        return url;
+    }
+
+    private String getIdentification() {
+        return m_agentContext.getIdentificationHandler().getIdentification();
+    }
+
+    private URL getServerURL() {
+        return m_agentContext.getDiscoveryHandler().getServerUrl();
+    }
+
+    private URLConnection getConnection(URL url) throws IOException {
+        return m_agentContext.getConnectionHandler().getConnection(url);
+    }
+
+    private static URL getEndpoint(URL serverURL, String identification) {
+        try {
+            return new URL(serverURL, "deployment/" + identification + "/versions/");
+        }
+        catch (MalformedURLException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private URL getEndpoint(URL serverURL, String identification, Version version) {
+        try {
+            return new URL(serverURL, "deployment/" + identification + "/versions/" + version.toString());
+        }
+        catch (MalformedURLException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private URL getEndpoint(URL serverURL, String identification, Version from, Version to) {
+        try {
+            return new URL(serverURL, "deployment/" + identification + "/versions/" + to.toString() + "?current=" + from);
+        }
+        catch (MalformedURLException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java Wed Aug 14 13:37:47 2013
@@ -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.ace.agent.impl;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ace.agent.DiscoveryHandler;
+
+/**
+ * Default discovery handler that reads the serverURL(s) from the configuration using key {@link DISCOVERY_CONFIG_KEY}.
+ * 
+ */
+public class DiscoveryHandlerImpl implements DiscoveryHandler {
+
+    /**
+     * Configuration key for the default discovery handler. The value must be a comma-separated list of valid base
+     * server URLs.
+     */
+    // TODO move to and validate in config handler?
+    public static final String DISCOVERY_CONFIG_KEY = "agent.discovery";
+
+    private final AgentContext m_agentContext;
+
+    public DiscoveryHandlerImpl(AgentContext agentContext) throws Exception {
+        m_agentContext = agentContext;
+    }
+
+    // TODO Pretty naive implementation below. It always takes the first configurred URL it can connect to and is not
+    // thread-safe.
+    @Override
+    public URL getServerUrl() {
+        String configValue = m_agentContext.getConfigurationHandler().getMap().get(DISCOVERY_CONFIG_KEY);
+        if (configValue == null || configValue.equals(""))
+            return null;
+        if (configValue.indexOf(",") == -1) {
+            return checkURL(configValue.trim());
+        }
+        for (String configValuePart : configValue.split(",")) {
+            URL url = checkURL(configValuePart.trim());
+            if (url != null)
+                return url;
+        }
+        return null;
+    }
+
+    private static final long CACHE_TIME = 1000;
+
+    private static class CheckedURL {
+        URL url;
+        long timestamp;
+
+        public CheckedURL(URL url, long timestamp) {
+            this.url = url;
+            this.timestamp = timestamp;
+        }
+    }
+
+    private final Map<String, CheckedURL> m_checkedURLs = new HashMap<String, DiscoveryHandlerImpl.CheckedURL>();
+
+    private URL checkURL(String serverURL) {
+        CheckedURL checked = m_checkedURLs.get(serverURL);
+        if (checked != null && checked.timestamp > (System.currentTimeMillis() - CACHE_TIME)) {
+            return checked.url;
+        }
+        try {
+            URL url = new URL(serverURL);
+            tryConnect(url);
+            m_checkedURLs.put(serverURL, new CheckedURL(url, System.currentTimeMillis()));
+            return url;
+        }
+        catch (IOException e) {
+            // TODO log
+            return null;
+        }
+    }
+
+    private void tryConnect(URL serverURL) throws IOException {
+        URLConnection connection = null;
+        try {
+            connection = m_agentContext.getConnectionHandler().getConnection(serverURL);
+            connection.connect();
+        }
+        finally {
+            if (connection != null && connection instanceof HttpURLConnection)
+                ((HttpURLConnection) connection).disconnect();
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,194 @@
+/*
+ * 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.ace.agent.impl;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+class DownloadCallableImpl implements Callable<Void> {
+
+    // test support
+    static final int FAIL_OPENCONNECTION = 1;
+    static final int FAIL_OPENINPUTSTREAM = 2;
+    static final int FAIL_OPENOUTPUTSTREAM = 3;
+    static final int FAIL_AFTERFIRSTWRITE = 4;
+
+    private final DownloadHandleImpl m_handle;
+    private final URL m_source;
+    private final File m_target;
+    private final int m_readBufferSize;
+    private final int m_failAtPosition;
+
+    private volatile boolean m_abort = false;
+
+    DownloadCallableImpl(DownloadHandleImpl handle, URL source, File target, int readBufferSize, int failAtPosition) {
+        m_handle = handle;
+        m_source = source;
+        m_target = target;
+        m_readBufferSize = readBufferSize;
+        m_failAtPosition = failAtPosition;
+    }
+
+    @Override
+    public Void call() throws Exception {
+        return download();
+    }
+
+    /**
+     * Abort the download. Used instead of a cancel on the future so normal completion can take place.
+     */
+    void abort() {
+        m_abort = true;
+    }
+
+    @SuppressWarnings("resource")
+    private Void download() {
+
+        int statusCode = 0;
+        Map<String, List<String>> headerFields = null;
+
+        BufferedInputStream inputStream = null;
+        BufferedOutputStream outputStream = null;
+        HttpURLConnection httpUrlConnection = null;
+        try {
+
+            boolean partialContent = false;
+            boolean appendTarget = false;
+
+            if (m_failAtPosition == FAIL_OPENCONNECTION)
+                throw new IOException("Failed on openConnection on request");
+            httpUrlConnection = (HttpURLConnection) m_source.openConnection();
+
+            long targetSize = m_target.length();
+            if (targetSize > 0) {
+                String rangeHeader = "bytes=" + targetSize + "-";
+                m_handle.logDebug("Requesting Range %s", targetSize, rangeHeader);
+                httpUrlConnection.setRequestProperty("Range", rangeHeader);
+            }
+
+            statusCode = httpUrlConnection.getResponseCode();
+            headerFields = httpUrlConnection.getHeaderFields();
+            if (statusCode == 200) {
+                partialContent = false;
+            }
+            else if (statusCode == 206) {
+                partialContent = true;
+            }
+            else {
+                throw new IOException("Unable to handle server response code " + statusCode);
+            }
+
+            if (m_failAtPosition == FAIL_OPENINPUTSTREAM)
+                throw new IOException("Failed on openConnection on request");
+            inputStream = new BufferedInputStream(httpUrlConnection.getInputStream());
+
+            long contentLength = httpUrlConnection.getContentLength();
+            if (partialContent) {
+                String contentRange = httpUrlConnection.getHeaderField("Content-Range");
+                if (contentRange == null) {
+                    throw new IOException("Server returned no Content-Range for partial content");
+                }
+                if (!contentRange.startsWith("bytes ")) {
+                    throw new IOException("Server returned non byes Content-Range " + contentRange);
+                }
+                String tmp = contentRange;
+                tmp = tmp.replace("byes ", "");
+                String[] parts = tmp.split("/");
+                String start = parts[0].split("-")[0];
+                String end = parts[0].split("-")[1];
+                System.out.println("size:" + parts[1]);
+                System.out.println("from:" + start);
+                System.out.println("too:" + end);
+
+                if (parts[1].equals("*"))
+                    contentLength = -1;
+                else
+                    contentLength = Long.parseLong(parts[1]);
+            }
+
+            long progress = 0l;
+            if (partialContent) {
+                progress = targetSize;
+                appendTarget = true;
+            }
+
+            if (m_failAtPosition == FAIL_OPENOUTPUTSTREAM)
+                throw new IOException("Failed on outputStream");
+            outputStream = new BufferedOutputStream(new FileOutputStream(m_target, appendTarget));
+
+            byte buffer[] = new byte[m_readBufferSize];
+            int read = -1;
+            while (!m_abort && (read = inputStream.read(buffer)) >= 0) {
+
+                outputStream.write(buffer, 0, read);
+                progress += read;
+                m_handle.progressCallback(statusCode, headerFields, contentLength, progress);
+
+                if (m_failAtPosition == FAIL_AFTERFIRSTWRITE)
+                    throw new IOException("Failed after first write");
+
+                if (Thread.currentThread().isInterrupted())
+                    m_abort = true;
+            }
+
+            if (m_abort) {
+                m_handle.logDebug("Download stopped");
+                m_handle.stoppedCallback(statusCode, headerFields, null);
+            }
+            else {
+                m_handle.logDebug("Download completed");
+                m_handle.successfulCallback(statusCode, headerFields);
+            }
+        }
+        catch (Exception e) {
+            m_handle.failedCallback(statusCode, headerFields, e);
+        }
+        cleanupQuietly(httpUrlConnection, inputStream, outputStream);
+        return null;
+    }
+
+    private static void cleanupQuietly(HttpURLConnection httpUrlConnection, InputStream inputStream, OutputStream outputStream) {
+        if (httpUrlConnection != null)
+            httpUrlConnection.disconnect();
+        if (inputStream != null)
+            try {
+                inputStream.close();
+            }
+            catch (IOException e) {
+                // ignore
+            }
+        if (outputStream != null)
+            try {
+                outputStream.close();
+            }
+            catch (IOException e) {
+                // ignore
+            }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandleImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandleImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandleImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandleImpl.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,195 @@
+/*
+ * 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.ace.agent.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+import org.apache.ace.agent.DownloadHandle;
+import org.apache.ace.agent.DownloadResult;
+import org.apache.ace.agent.DownloadState;
+
+/**
+ * A {@link DownloadHandle} implementation that supports pause/resume semantics based on HTTP Range headers assuming the
+ * server supports this feature.
+ * 
+ */
+class DownloadHandleImpl implements DownloadHandle {
+
+    private final DownloadHandlerImpl m_handler;
+    private final URL m_url;
+    private final int m_readBufferSize;
+
+    private volatile boolean m_started = false;
+    private volatile boolean m_completed = false;
+
+    private volatile Future<Void> m_future;
+    private volatile DownloadCallableImpl m_callable;
+    private volatile File m_file;
+
+    private volatile ProgressListener m_progressListener;
+    private volatile CompletedListener m_completionListener;
+
+    private volatile DownloadResult m_downloadResult;
+
+    DownloadHandleImpl(DownloadHandlerImpl handler, URL url) {
+        this(handler, url, DEFAULT_READBUFFER_SIZE);
+    }
+
+    DownloadHandleImpl(DownloadHandlerImpl handler, URL url, int readBufferSize) {
+        m_handler = handler;
+        m_url = url;
+        m_readBufferSize = readBufferSize;
+    }
+
+    @Override
+    public DownloadHandle setProgressListener(ProgressListener listener) {
+        m_progressListener = listener;
+        return this;
+    }
+
+    @Override
+    public DownloadHandle setCompletionListener(CompletedListener listener) {
+        m_completionListener = listener;
+        return this;
+    }
+
+    @Override
+    public DownloadHandle start() {
+        return start(-1);
+    }
+
+    DownloadHandle start(int failAtPosition) {
+        if (m_started)
+            throw new IllegalStateException("Can not call start on a handle that is allready started");
+        if (m_file == null) {
+            try {
+                m_file = File.createTempFile("download", ".bin");
+            }
+            catch (IOException e) {
+                failedCallback(0, null, e);
+            }
+        }
+        startDownload(failAtPosition);
+        return this;
+    }
+
+    @Override
+    public DownloadHandle stop() {
+        if (!m_started && !m_completed)
+            throw new IllegalStateException("Can not call stop on a handle that is not yet started");
+        m_started = false;
+        stopDownload();
+        return this;
+    }
+
+    @Override
+    public DownloadResult result() {
+        if (m_completed)
+            return m_downloadResult;
+        if (!m_started)
+            throw new IllegalStateException("Can not call result on a handle that is not yet started");
+        try {
+            m_future.get();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+        return m_downloadResult;
+    }
+
+    @Override
+    public void discard() {
+        if (m_started)
+            stop();
+        m_file.delete();
+    }
+
+    void progressCallback(int statusCode, Map<String, List<String>> headers, long contentLength, long progress) {
+        callProgressListener(m_progressListener, contentLength, progress);
+    }
+
+    void successfulCallback(int statusCode, Map<String, List<String>> headers) {
+        m_started = false;
+        m_completed = true;
+        m_downloadResult = new DownloadResultImpl(DownloadState.SUCCESSFUL, m_file, statusCode, headers, null);
+        callCompletionListener(m_completionListener, m_downloadResult);
+    }
+
+    void stoppedCallback(int statusCode, Map<String, List<String>> headers, Throwable cause) {
+        m_started = false;
+        m_completed = false;
+        m_downloadResult = new DownloadResultImpl(DownloadState.STOPPED, null, statusCode, headers, cause);
+        callCompletionListener(m_completionListener, m_downloadResult);
+    }
+
+    void failedCallback(int statusCode, Map<String, List<String>> headers, Throwable cause) {
+        m_started = false;
+        m_completed = false;
+        m_downloadResult = new DownloadResultImpl(DownloadState.FAILED, null, statusCode, headers, cause);
+        callCompletionListener(m_completionListener, m_downloadResult);
+    }
+
+    void logDebug(String message, Object... args) {
+        m_handler.logDebug(message, args);
+    }
+
+    void logInfo(String message, Object... args) {
+        m_handler.logInfo(message, args);
+    }
+
+    void logWarning(String message, Object... args) {
+        m_handler.logWarning(message, args);
+    }
+
+    private void startDownload(int failAtPosition) {
+        m_started = true;
+        m_callable = new DownloadCallableImpl(this, m_url, m_file, m_readBufferSize, failAtPosition);
+        m_future = m_handler.getExecutor().submit(m_callable);
+    }
+
+    private void stopDownload() {
+        m_started = false;
+        m_callable.abort();
+    }
+
+    private static void callProgressListener(ProgressListener listener, long contentLength, long progress) {
+        if (listener != null)
+            try {
+                listener.progress(contentLength, progress);
+            }
+            catch (Exception e) {
+                // ignore
+            }
+    }
+
+    private static void callCompletionListener(CompletedListener listener, DownloadResult result) {
+        if (listener != null && result != null)
+            try {
+                listener.completed(result);
+            }
+            catch (Exception e) {
+                // ignore
+            }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandlerImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandlerImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandlerImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadHandlerImpl.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,63 @@
+/*
+ * 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.ace.agent.impl;
+
+import java.net.URL;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.ace.agent.DownloadHandle;
+import org.apache.ace.agent.DownloadHandler;
+
+public class DownloadHandlerImpl implements DownloadHandler {
+
+    private final AgentContext m_agentContext;
+
+    public DownloadHandlerImpl(AgentContext agentContext) {
+        m_agentContext = agentContext;
+    }
+
+    @Override
+    public DownloadHandle getHandle(URL url) {
+        return new DownloadHandleImpl(this, url);
+    }
+
+    @Override
+    public DownloadHandle getHandle(URL url, int readBufferSize) {
+        return new DownloadHandleImpl(this, url, readBufferSize);
+    }
+
+    /*
+     * handle support methods
+     */
+    ExecutorService getExecutor() {
+        return m_agentContext.getExecutorService();
+    }
+
+    void logDebug(String message, Object... args) {
+        System.err.println(String.format(message, args));
+    }
+
+    void logInfo(String message, Object... args) {
+        System.err.println(String.format(message, args));
+    }
+
+    void logWarning(String message, Object... args) {
+        System.err.println(String.format(message, args));
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadResultImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadResultImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadResultImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadResultImpl.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,68 @@
+/*
+ * 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.ace.agent.impl;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ace.agent.DownloadResult;
+import org.apache.ace.agent.DownloadState;
+
+public class DownloadResultImpl implements DownloadResult {
+
+    final DownloadState m_state;
+    final File m_file;
+    final int m_code;
+    final Map<String, List<String>> m_headers;
+    final Throwable m_cause;
+
+    DownloadResultImpl(DownloadState state, File file, int code, Map<String, List<String>> headers, Throwable cause) {
+        m_state = state;
+        m_file = file;
+        m_code = code;
+        m_headers = headers;
+        m_cause = cause;
+    }
+
+    @Override
+    public DownloadState getState() {
+        return m_state;
+    }
+
+    @Override
+    public File getFile() {
+        return m_file;
+    }
+
+    @Override
+    public int getCode() {
+        return m_code;
+    }
+
+    @Override
+    public Map<String, List<String>> getHeaders() {
+        return m_headers;
+    }
+
+    @Override
+    public Throwable getCause() {
+        return m_cause;
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/IdentificationHandlerImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/IdentificationHandlerImpl.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/IdentificationHandlerImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/IdentificationHandlerImpl.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,36 @@
+package org.apache.ace.agent.impl;
+
+import org.apache.ace.agent.IdentificationHandler;
+
+/**
+ * Default identification handler that reads the identity from the configuration using key
+ * {@link IDENTIFICATION_CONFIG_KEY}.
+ * 
+ */
+public class IdentificationHandlerImpl implements IdentificationHandler {
+
+    /**
+     * Configuration key for the default identification handler. The value must be a single file-system and URL safe
+     * string.
+     */
+    // TODO move to and validate in configuration handler?
+    public static final String IDENTIFICATION_CONFIG_KEY = "agent.discovery";
+
+    private final AgentContext m_agentContext;
+
+    public IdentificationHandlerImpl(AgentContext agentContext) {
+        m_agentContext = agentContext;
+    }
+
+    // TODO add a default fallback?
+    @Override
+    public String getIdentification() {
+        String configValue = m_agentContext.getConfigurationHandler().getMap().get(IDENTIFICATION_CONFIG_KEY);
+        if (configValue == null)
+            return null;
+        configValue = configValue.trim();
+        if (configValue.equals(""))
+            return null;
+        return configValue;
+    }
+}

Added: ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ConnectionHandlerImplTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ConnectionHandlerImplTest.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ConnectionHandlerImplTest.java (added)
+++ ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ConnectionHandlerImplTest.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,118 @@
+/*
+ * 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.ace.agent.impl;
+
+import static org.easymock.EasyMock.expect;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ace.agent.ConfigurationHandler;
+import org.apache.ace.agent.ConnectionHandler;
+import org.apache.ace.agent.testutil.BaseAgentTest;
+import org.apache.ace.agent.testutil.TestWebServer;
+import org.apache.commons.codec.binary.Base64;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ * Testing {@link ConnectionHandlerImpl}
+ * 
+ */
+// TODO test CLIENT_CERT
+public class ConnectionHandlerImplTest extends BaseAgentTest {
+
+    static class BasicAuthServlet extends HttpServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        private final String m_authHeader;
+
+        public BasicAuthServlet(String username, String password) {
+            m_authHeader = "Basic " + new String(Base64.encodeBase64((username + ":" + password).getBytes()));
+        }
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+            String authHeader = req.getHeader("Authorization");
+            if (authHeader == null || !authHeader.equals(m_authHeader))
+                resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Requires Basic Auth");
+            resp.setStatus(HttpServletResponse.SC_OK);
+        }
+
+    }
+
+    Map<String, String> m_configuration = new HashMap<String, String>();
+    ConnectionHandler m_connectionHandler;
+    TestWebServer m_webServer;
+    String m_user = "Mickey";
+    String m_pass = "Mantle";
+    URL m_basicAuthURL;
+
+    @BeforeTest
+    public void setUpAgain() throws Exception {
+
+        int port = 8880;
+        m_basicAuthURL = new URL("http://localhost:" + port + "/basicauth");
+        m_webServer = new TestWebServer(port, "/", "generated");
+        m_webServer.addServlet(new BasicAuthServlet(m_user, m_pass), "/basicauth/*");
+        m_webServer.start();
+
+        ConfigurationHandler configurationHandler = addTestMock(ConfigurationHandler.class);
+        expect(configurationHandler.getMap()).andReturn(m_configuration).anyTimes();
+
+        AgentContext agentContext = addTestMock(AgentContext.class);
+        expect(agentContext.getConfigurationHandler()).andReturn(configurationHandler).anyTimes();
+
+        replayTestMocks();
+        m_connectionHandler = new ConnectionHandlerImpl(agentContext);
+    }
+
+    @AfterTest
+    public void tearDownAgain() throws Exception {
+        m_webServer.stop();
+        verifyTestMocks();
+    }
+
+    @Test
+    public void testBasicAuthFORBIDDEN() throws Exception {
+        m_configuration.clear();
+        HttpURLConnection connection = (HttpURLConnection) m_connectionHandler.getConnection(m_basicAuthURL);
+        assertEquals(connection.getResponseCode(), HttpServletResponse.SC_FORBIDDEN);
+    }
+
+    @Test
+    public void testBasicAuthOK() throws Exception {
+        m_configuration.put(ConnectionHandlerImpl.PROP_AUTHTYPE, "BASIC");
+        m_configuration.put(ConnectionHandlerImpl.PROP_AUTHUSER, m_user);
+        m_configuration.put(ConnectionHandlerImpl.PROP_AUTHPASS, m_pass);
+        HttpURLConnection connection = (HttpURLConnection) m_connectionHandler.getConnection(m_basicAuthURL);
+        assertEquals(connection.getResponseCode(), HttpServletResponse.SC_OK);
+    }
+}

Added: ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/CustomControllerTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/CustomControllerTest.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/CustomControllerTest.java (added)
+++ ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/CustomControllerTest.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,123 @@
+/*
+ * 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.ace.agent.impl;
+
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.notNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.ace.agent.AgentControl;
+import org.apache.ace.agent.DeploymentHandler;
+import org.apache.ace.agent.DownloadHandle;
+import org.apache.ace.agent.DownloadResult;
+import org.apache.ace.agent.DownloadState;
+import org.apache.ace.agent.testutil.BaseAgentTest;
+import org.osgi.framework.Version;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+public class CustomControllerTest extends BaseAgentTest {
+
+    AgentControl m_agentControl;
+    Version m_version1 = Version.parseVersion("1.0.0");
+    Version m_version2 = Version.parseVersion("2.0.0");
+    Version m_version3 = Version.parseVersion("3.0.0");
+    SortedSet<Version> m_availableVersions = new TreeSet<Version>();
+    File m_dummyFile;
+    URL m_dummyFileUrl;
+    InputStream m_dummyInputStream;
+
+    @BeforeTest
+    public void setUpOnceAgain() throws Exception {
+
+        m_availableVersions.add(m_version1);
+        m_availableVersions.add(m_version2);
+        m_availableVersions.add(m_version3);
+
+        m_dummyFile = File.createTempFile("mock", ".txt");
+        m_dummyFile.deleteOnExit();
+        m_dummyFileUrl = m_dummyFile.toURI().toURL();
+    }
+
+    @BeforeMethod
+    public void setUpAgain() throws Exception {
+
+        m_dummyInputStream = new FileInputStream(m_dummyFile);
+
+        DownloadResult downloadResult = addTestMock(DownloadResult.class);
+        expect(downloadResult.getState()).andReturn(DownloadState.SUCCESSFUL).anyTimes();
+        expect(downloadResult.getFile()).andReturn(m_dummyFile).anyTimes();
+
+        DownloadHandle downloadHandle = addTestMock(DownloadHandle.class);
+        expect(downloadHandle.start()).andReturn(downloadHandle).anyTimes();
+        expect(downloadHandle.result()).andReturn(downloadResult).anyTimes();
+
+        DeploymentHandler deploymentHandler = addTestMock(DeploymentHandler.class);
+        expect(deploymentHandler.getInstalledVersion()).andReturn(m_version2).anyTimes();
+        expect(deploymentHandler.getAvailableVersions()).andReturn(m_availableVersions).anyTimes();
+        expect(deploymentHandler.getDownloadHandle(eq(m_version3), eq(true))).andReturn(downloadHandle).once();
+        deploymentHandler.deployPackage(notNull(InputStream.class));
+        expectLastCall().once();
+
+        AgentContext agentContext = addTestMock(AgentContext.class);
+        expect(agentContext.getDeploymentHandler()).andReturn(deploymentHandler).anyTimes();
+
+        replayTestMocks();
+        m_agentControl = new AgentControlImpl(agentContext);
+    }
+
+    @AfterMethod
+    public void tearDownOnceAgain() throws Exception {
+        verifyTestMocks();
+        clearTestMocks();
+        m_dummyInputStream.close();
+    }
+
+    @Test
+    public void testDowlownloading() throws Exception {
+
+        Version current = m_agentControl.getDeploymentHandler().getInstalledVersion();
+        Version highest = m_agentControl.getDeploymentHandler().getAvailableVersions().last();
+        if (highest.compareTo(current) > 0) {
+
+            DownloadHandle handle = m_agentControl.getDeploymentHandler().getDownloadHandle(highest, true);
+            DownloadResult result = handle.start().result();
+
+            if (result.getState() == DownloadState.SUCCESSFUL) {
+                InputStream inputStream = new FileInputStream(result.getFile());
+                try {
+                    m_agentControl.getDeploymentHandler().deployPackage(inputStream);
+                }
+                finally {
+                    inputStream.close();
+                }
+            }
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DeploymentHandlerTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DeploymentHandlerTest.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DeploymentHandlerTest.java (added)
+++ ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DeploymentHandlerTest.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,214 @@
+/*
+ * 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.ace.agent.impl;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.notNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ace.agent.AgentConstants;
+import org.apache.ace.agent.ConfigurationHandler;
+import org.apache.ace.agent.DiscoveryHandler;
+import org.apache.ace.agent.IdentificationHandler;
+import org.apache.ace.agent.testutil.BaseAgentTest;
+import org.apache.ace.agent.testutil.TestWebServer;
+import org.osgi.framework.Version;
+import org.osgi.service.deploymentadmin.DeploymentAdmin;
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+public class DeploymentHandlerTest extends BaseAgentTest {
+
+    static class TestDeploymentServlet extends HttpServlet {
+        private static final long serialVersionUID = 1L;
+
+        private Map<String, File> m_packages = new HashMap<String, File>();
+
+        @SuppressWarnings("deprecation")
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+            String pathInfo = req.getPathInfo();
+            if (pathInfo != null && pathInfo.startsWith("/"))
+                pathInfo = pathInfo.substring(1);
+
+            if (pathInfo != null && m_packages.containsKey(pathInfo)) {
+                File file = m_packages.get(pathInfo);
+                resp.setHeader(AgentConstants.HEADER_DPSIZE, "" + file.length());
+                FileInputStream fis = new FileInputStream(file);
+                OutputStream os = resp.getOutputStream();
+                int b;
+                while ((b = fis.read()) != -1) {
+                    os.write(b);
+                }
+                fis.close();
+                os.close();
+            }
+            else {
+                PrintWriter writer = resp.getWriter();
+                for (String version : m_packages.keySet()) {
+                    writer.println(version);
+                }
+                writer.close();
+            }
+            resp.setStatus(200, "voila");
+        }
+
+        void addPackage(String version, File file) {
+            m_packages.put(version, file);
+        }
+    }
+
+    private DeploymentHandlerImpl m_deploymentHandler;
+    private TestWebServer m_webserver;
+    private File m_200file;
+    private Version m_version1 = Version.parseVersion("1.0.0");
+    private Version m_version2 = Version.parseVersion("2.0.0");
+    private Version m_version3 = Version.parseVersion("3.0.0");
+    long m_remotePackageSize = 0l;
+
+    @BeforeTest
+    public void setUpOnceAgain() throws Exception {
+
+        int port = 8881;
+        String identification = "agent";
+        URL serverURL = new URL("http://localhost:" + port + "/");
+
+        m_webserver = new TestWebServer(port, "/", "generated");
+        m_webserver.start();
+
+        m_200file = new File(new File("generated"), "testfile.txt");
+        DigestOutputStream dos = new DigestOutputStream(new FileOutputStream(m_200file), MessageDigest.getInstance("MD5"));
+        for (int i = 0; i < 10000; i++) {
+            dos.write(String.valueOf(System.currentTimeMillis()).getBytes());
+            dos.write(" Lorum Ipsum Lorum Ipsum Lorum Ipsum Lorum Ipsum Lorum Ipsum\n".getBytes());
+        }
+        dos.close();
+
+        TestDeploymentServlet servlet = new TestDeploymentServlet();
+        servlet.addPackage(m_version1.toString(), m_200file);
+        servlet.addPackage(m_version2.toString(), m_200file);
+        servlet.addPackage(m_version3.toString(), m_200file);
+
+        m_remotePackageSize = m_200file.length();
+        m_webserver.addServlet(servlet, "/deployment/" + identification + "/versions/*");
+
+        DeploymentPackage deploymentPackage1 = addTestMock(DeploymentPackage.class);
+        expect(deploymentPackage1.getName()).andReturn(identification).anyTimes();
+        expect(deploymentPackage1.getVersion()).andReturn(Version.parseVersion("1.0.0")).anyTimes();
+
+        DeploymentPackage deploymentPackage2 = addTestMock(DeploymentPackage.class);
+        expect(deploymentPackage2.getName()).andReturn(identification).anyTimes();
+        expect(deploymentPackage2.getVersion()).andReturn(Version.parseVersion("2.0.0")).anyTimes();
+
+        DeploymentPackage deploymentPackage3 = addTestMock(DeploymentPackage.class);
+        expect(deploymentPackage3.getName()).andReturn(identification).anyTimes();
+        expect(deploymentPackage3.getVersion()).andReturn(Version.parseVersion("3.0.0")).anyTimes();
+
+        IdentificationHandler identificationHandler = addTestMock(IdentificationHandler.class);
+        expect(identificationHandler.getIdentification()).andReturn(identification).anyTimes();
+
+        DiscoveryHandler discoveryHandler = addTestMock(DiscoveryHandler.class);
+        expect(discoveryHandler.getServerUrl()).andReturn(serverURL).anyTimes();
+
+        ConfigurationHandler configurationHandler = addTestMock(ConfigurationHandler.class);
+        expect(configurationHandler.getMap()).andReturn(new HashMap<String, String>()).anyTimes();
+
+        DeploymentAdmin deploymentAdmin = addTestMock(DeploymentAdmin.class);
+        expect(deploymentAdmin.listDeploymentPackages()).andReturn(
+            new DeploymentPackage[] { deploymentPackage2, deploymentPackage1 }).anyTimes();
+        expect(deploymentAdmin.installDeploymentPackage(notNull(InputStream.class)
+            )).andReturn(deploymentPackage3).once();
+
+        AgentContext agentContext = addTestMock(AgentContext.class);
+        expect(agentContext.getIdentificationHandler()).andReturn(identificationHandler).anyTimes();
+        expect(agentContext.getDiscoveryHandler()).andReturn(discoveryHandler).anyTimes();
+        expect(agentContext.getConfigurationHandler()).andReturn(configurationHandler).anyTimes();
+        expect(agentContext.getConnectionHandler()).andReturn(new ConnectionHandlerImpl(agentContext)).anyTimes();
+
+        replayTestMocks();
+        m_deploymentHandler = new DeploymentHandlerImpl(agentContext, deploymentAdmin);
+    }
+
+    @AfterTest
+    public void tearDownOnceAgain() throws Exception {
+        verifyTestMocks();
+        m_webserver.stop();
+    }
+
+    @Test
+    public void testCurrentVersion() throws Exception {
+        Version current = m_deploymentHandler.getInstalledVersion();
+        assertNotNull(current);
+        assertEquals(current, m_version2);
+    }
+
+    @Test
+    public void testAvailableVersions() throws Exception {
+        SortedSet<Version> expected = new TreeSet<Version>();
+        expected.add(m_version1);
+        expected.add(m_version2);
+        expected.add(m_version3);
+        SortedSet<Version> available = m_deploymentHandler.getAvailableVersions();
+        assertNotNull(available);
+        assertFalse(available.isEmpty());
+        assertEquals(available, expected);
+    }
+
+    @Test
+    public void testPackageSize() throws Exception {
+        long packageSize = m_deploymentHandler.getPackageSize(m_version1, true);
+        assertEquals(packageSize, m_remotePackageSize);
+    }
+
+    @Test
+    public void testDeployPackage() throws Exception {
+        InputStream inputStream = m_deploymentHandler.getInputStream(m_version3, true);
+        try {
+            m_deploymentHandler.deployPackage(inputStream);
+        }
+        finally {
+            inputStream.close();
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DiscoveryHandlerImplTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DiscoveryHandlerImplTest.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DiscoveryHandlerImplTest.java (added)
+++ ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DiscoveryHandlerImplTest.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,134 @@
+/*
+ * 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.ace.agent.impl;
+
+import static org.easymock.EasyMock.expect;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ace.agent.ConfigurationHandler;
+import org.apache.ace.agent.ConnectionHandler;
+import org.apache.ace.agent.DiscoveryHandler;
+import org.apache.ace.agent.impl.AgentContext;
+import org.apache.ace.agent.impl.ConnectionHandlerImpl;
+import org.apache.ace.agent.impl.DiscoveryHandlerImpl;
+import org.apache.ace.agent.testutil.BaseAgentTest;
+import org.apache.ace.agent.testutil.TestWebServer;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+public class DiscoveryHandlerImplTest extends BaseAgentTest {
+
+    Map<String, String> configuration = new HashMap<String, String>();
+
+    DiscoveryHandler m_discoveryHandler;
+
+    TestWebServer m_webServer;
+    TestWebServer m_secondWebServer;
+
+    URL m_availableURL;
+    URL m_unavailableURL;
+
+    @BeforeTest
+    public void setUpAgain() throws Exception {
+
+        int port = 8882;
+        m_webServer = new TestWebServer(port, "/", "generated");
+        m_webServer.start();
+
+        m_availableURL = new URL("http://localhost:" + port);
+        m_unavailableURL = new URL("http://localhost:9999");
+
+        AgentContext agentContext = addTestMock(AgentContext.class);
+        m_discoveryHandler = new DiscoveryHandlerImpl(agentContext);
+
+        ConfigurationHandler configurationHandler = addTestMock(ConfigurationHandler.class);
+        expect(configurationHandler.getMap()).andReturn(configuration).anyTimes();
+
+        ConnectionHandler connectionHandler = new ConnectionHandlerImpl(agentContext);
+
+        expect(agentContext.getConfigurationHandler()).andReturn(configurationHandler).anyTimes();
+        expect(agentContext.getConnectionHandler()).andReturn(connectionHandler).anyTimes();
+
+        replayTestMocks();
+    }
+
+    @AfterTest
+    public void tearDownAgain() throws Exception {
+        m_webServer.stop();
+        verifyTestMocks();
+    }
+
+    @Test
+    public void testAvailableURL() throws Exception {
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, m_availableURL.toExternalForm());
+        assertEquals(m_discoveryHandler.getServerUrl(), m_availableURL);
+    }
+
+    @Test
+    public void testUnavailableURL_unavailable() throws Exception {
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, m_unavailableURL.toExternalForm());
+        assertNull(m_discoveryHandler.getServerUrl());
+    }
+
+    @Test
+    public void testUnavailableAfterConfigUpdate() throws Exception {
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, m_availableURL.toExternalForm());
+        assertEquals(m_discoveryHandler.getServerUrl(), m_availableURL);
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, m_unavailableURL.toExternalForm());
+        assertNull(m_discoveryHandler.getServerUrl());
+    }
+
+    @Test
+    public void testAvailableAfterConfigUpdate() throws Exception {
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, m_unavailableURL.toExternalForm());
+        assertNull(m_discoveryHandler.getServerUrl());
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, m_availableURL.toExternalForm());
+        assertEquals(m_discoveryHandler.getServerUrl(), m_availableURL);
+    }
+
+    @Test
+    public void testAvailableAfterUnavailableURL() throws Exception {
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, m_unavailableURL.toExternalForm() + "," + m_availableURL.toExternalForm());
+        assertEquals(m_discoveryHandler.getServerUrl(), m_availableURL);
+    }
+
+    @Test
+    public void testNoURLConfig() throws Exception {
+        configuration.clear();
+        assertNull(m_discoveryHandler.getServerUrl());
+    }
+
+    @Test
+    public void testEmptyURLConfig() throws Exception {
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, "");
+        assertNull(m_discoveryHandler.getServerUrl());
+    }
+
+    @Test
+    public void testBadURLConfig() throws Exception {
+        configuration.put(DiscoveryHandlerImpl.DISCOVERY_CONFIG_KEY, "fooBar");
+        assertNull(m_discoveryHandler.getServerUrl());
+    }
+}

Added: ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java?rev=1513878&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java (added)
+++ ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java Wed Aug 14 13:37:47 2013
@@ -0,0 +1,233 @@
+/*
+ * 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.ace.agent.impl;
+
+import static org.easymock.EasyMock.expect;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.URL;
+import java.security.DigestInputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ace.agent.DownloadHandle;
+import org.apache.ace.agent.DownloadHandle.CompletedListener;
+import org.apache.ace.agent.DownloadHandle.ProgressListener;
+import org.apache.ace.agent.DownloadResult;
+import org.apache.ace.agent.DownloadState;
+import org.apache.ace.agent.testutil.BaseAgentTest;
+import org.apache.ace.agent.testutil.TestWebServer;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+public class DownloadHandlerTest extends BaseAgentTest {
+
+    static class TestErrorServlet extends HttpServlet {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
+            String retry = req.getParameter("retry");
+            if (retry != null)
+                ((HttpServletResponse) res).setHeader("Retry-After", retry);
+            int code = 500;
+            String status = req.getParameter("status");
+            if (status != null)
+                code = Integer.parseInt(status);
+            ((HttpServletResponse) res).sendError(code, "You asked for it");
+        }
+    }
+
+    private DownloadHandlerImpl m_handler;
+    private TestWebServer m_webServer;
+    private URL m_200url;
+    private File m_200file;
+    private String m_200digest;
+
+    private URL m_404url;
+    private URL m_503url;
+
+    @BeforeTest
+    public void setUpOnceAgain() throws Exception {
+
+        int port = 8883;
+
+        m_200url = new URL("http://localhost:" + port + "/testfile.txt");
+        m_404url = new URL("http://localhost:" + port + "/error?status=404");
+        m_503url = new URL("http://localhost:" + port + "/error?status=503&retry=500");
+
+        m_200file = new File(new File("generated"), "testfile.txt");
+        DigestOutputStream dos = new DigestOutputStream(new FileOutputStream(m_200file), MessageDigest.getInstance("MD5"));
+        for (int i = 0; i < 10000; i++) {
+            dos.write(String.valueOf(System.currentTimeMillis()).getBytes());
+            dos.write(" Lorum Ipsum Lorum Ipsum Lorum Ipsum Lorum Ipsum Lorum Ipsum\n".getBytes());
+        }
+        dos.close();
+        m_200digest = new BigInteger(dos.getMessageDigest().digest()).toString();
+
+        m_webServer = new TestWebServer(port, "/", "generated");
+        m_webServer.addServlet(new TestErrorServlet(), "/error");
+        m_webServer.start();
+
+        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+        AgentContext agentContext = addTestMock(AgentContext.class);
+        expect(agentContext.getExecutorService()).andReturn(executorService).anyTimes();
+
+        replayTestMocks();
+        m_handler = new DownloadHandlerImpl(agentContext);
+    }
+
+    @AfterTest
+    public void tearDownOnceAgain() throws Exception {
+        verifyTestMocks();
+        m_webServer.stop();
+    }
+
+    @Test
+    public void testSuccessful_noresume_result() throws Exception {
+        final DownloadHandle handle = m_handler.getHandle(m_200url).start();
+        final DownloadResult result = handle.result();
+        assertSuccessFul(result, 200, m_200digest);
+    }
+
+    @Test
+    public void testSuccessful_noresume_listener() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final List<DownloadResult> holder = new ArrayList<DownloadResult>();
+        final DownloadHandle handle = m_handler.getHandle(m_200url)
+            .setCompletionListener(new CompletedListener() {
+                @Override
+                public void completed(DownloadResult result) {
+                    holder.add(result);
+                    latch.countDown();
+                }
+            }).start();
+        latch.await();
+        assertSuccessFul(holder.get(0), 200, m_200digest);
+        assertSame(handle.result(), holder.get(0), "Await should return same result given to the completion handler.");
+    }
+
+    @Test
+    public void testSuccessful_resume_result() throws Exception {
+        final DownloadHandle handle = m_handler.getHandle(m_200url);
+        handle.setProgressListener(new ProgressListener() {
+            @Override
+            public void progress(long contentLength, long progress) {
+                handle.stop();
+            }
+        }).start();
+        assertStopped(handle.result(), 200);
+        assertStopped(handle.start().result(), 206);
+        assertSuccessFul(handle.setProgressListener(null).start().result(), 206, m_200digest);
+    }
+
+    @Test
+    public void testFailedIO_nostatus_result() throws Exception {
+        DownloadHandle handle = m_handler.getHandle(m_200url, 2048);
+
+        DownloadResult result = ((DownloadHandleImpl) handle).start(DownloadCallableImpl.FAIL_OPENCONNECTION).result();
+        assertFailed(result, 0);
+        assertNull(result.getHeaders());
+
+        result = ((DownloadHandleImpl) handle).start(DownloadCallableImpl.FAIL_OPENINPUTSTREAM).result();
+        assertFailed(result, 200);
+        assertNotNull(result.getHeaders());
+
+        result = ((DownloadHandleImpl) handle).start(DownloadCallableImpl.FAIL_OPENOUTPUTSTREAM).result();
+        assertFailed(result, 200);
+        assertNotNull(result.getHeaders());
+
+        result = ((DownloadHandleImpl) handle).start(DownloadCallableImpl.FAIL_AFTERFIRSTWRITE).result();
+        assertFailed(result, 200);
+        assertNotNull(result.getHeaders());
+
+        result = ((DownloadHandleImpl) handle).start(DownloadCallableImpl.FAIL_AFTERFIRSTWRITE).result();
+        assertFailed(result, 206);
+        assertNotNull(result.getHeaders());
+
+        result = handle.start().result();
+        assertSuccessFul(result, 206, m_200digest);
+    }
+
+    @Test
+    public void testFailed404_noresume_result() throws Exception {
+        final DownloadResult result = m_handler.getHandle(m_404url).start().result();
+        assertFailed(result, 404);
+    }
+
+    @Test
+    public void testFailed503_noresume_result() throws Exception {
+        DownloadResult result = m_handler.getHandle(m_503url).start().result();
+        assertFailed(result, 503);
+        assertNotNull(result.getHeaders().get("Retry-After"), "Expected a Retry-After header from error servlet");
+        assertNotNull(result.getHeaders().get("Retry-After").get(0), "Expected a Retry-After header from error servlet");
+        assertEquals(result.getHeaders().get("Retry-After").get(0), "500", "Expected a Retry-After header from error servlet");
+    }
+
+    private static void assertSuccessFul(final DownloadResult result, int statusCode, String digest) throws Exception {
+        assertEquals(result.getState(), DownloadState.SUCCESSFUL, "Expected state SUCCESSFUL after succesful completion");
+        assertEquals(result.getCode(), statusCode, "Expected statusCode " + statusCode + " after successful completion");
+        assertNotNull(result.getFile(), "Expected non null file after successful completion");
+        assertNotNull(result.getHeaders(), "Expected non null headers after successful completion");
+        assertNull(result.getCause(), "Excpected null cause after successful completion");
+        assertEquals(getDigest(result.getFile()), digest, "Expected same digest after successful completion");
+    }
+
+    private static void assertFailed(final DownloadResult result, int statusCode) throws Exception {
+        assertEquals(result.getState(), DownloadState.FAILED, "DownloadState must be FAILED after failed completion");
+        assertEquals(result.getCode(), statusCode, "Expected statusCode " + statusCode + " after failed completion");
+        assertNull(result.getFile(), "File must not be null after failed completion");
+    }
+
+    private static void assertStopped(final DownloadResult result, int statusCode) throws Exception {
+        assertEquals(result.getState(), DownloadState.STOPPED, "DownloadState must be STOPPED after stopped completion");
+        assertEquals(result.getCode(), statusCode, "Expected statusCode " + statusCode + " after stopped completion");
+        assertNotNull(result.getHeaders(), "Expected headers not to be null after stopped completion");
+        assertNull(result.getFile(), "File must not be null after failed download");
+        assertNull(result.getCause(), "Excpected cause to null null after stopped completion");
+    }
+
+    private static String getDigest(File file) throws Exception {
+        DigestInputStream dis = new DigestInputStream(new FileInputStream(file), MessageDigest.getInstance("MD5"));
+        while (dis.read() != -1) {
+        }
+        dis.close();
+        return new BigInteger(dis.getMessageDigest().digest()).toString();
+    }
+}



Mime
View raw message