You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/17 21:18:11 UTC

[40/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/ResourceUtils.java b/core/src/main/java/brooklyn/util/ResourceUtils.java
deleted file mode 100644
index 338b223..0000000
--- a/core/src/main/java/brooklyn/util/ResourceUtils.java
+++ /dev/null
@@ -1,639 +0,0 @@
-/*
- * 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 brooklyn.util;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.JarURLConnection;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
-import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.auth.Credentials;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpTool.HttpClientBuilder;
-import brooklyn.util.javalang.Threads;
-import brooklyn.util.net.Urls;
-import brooklyn.util.os.Os;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.DataUriSchemeParser;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-
-public class ResourceUtils {
-    
-    private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class);
-    private static final List<Function<Object,BrooklynClassLoadingContext>> classLoaderProviders = Lists.newCopyOnWriteArrayList();
-
-    private BrooklynClassLoadingContext loader = null;
-    private String context = null;
-    private Object contextObject = null;
-    
-    static { BrooklynInitialization.initNetworking(); }
-    
-    /**
-     * Creates a {@link ResourceUtils} object with a specific class loader and context.
-     * <p>
-     * Use the provided {@link ClassLoader} object for class loading with the
-     * {@code contextObject} for context and the {@code contextMessage} string for
-     * error messages.
-     *
-     * @see ResourceUtils#create(Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(ClassLoader loader, Object contextObject, String contextMessage) {
-        return new ResourceUtils(loader, contextObject, contextMessage);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with a specific class loader and context.
-     * <p>
-     * Use the provided {@link BrooklynClassLoadingContext} object for class loading with the
-     * {@code contextObject} for context and the {@code contextMessage} string for
-     * error messages.
-     *
-     * @see ResourceUtils#create(Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) {
-        return new ResourceUtils(loader, contextObject, contextMessage);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with the given context.
-     * <p>
-     * Uses the {@link ClassLoader} of the given {@code contextObject} for class
-     * loading and the {@code contextMessage} string for error messages.
-     *
-     * @see ResourceUtils#create(ClassLoader, Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(Object contextObject, String contextMessage) {
-        return new ResourceUtils(contextObject, contextMessage);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with the given context.
-     * <p>
-     * Uses the {@link ClassLoader} of the given {@code contextObject} for class
-     * loading and its {@link Object#toString()} (preceded by the word 'for') as
-     * the string used in error messages.
-     *
-     * @see ResourceUtils#create(ClassLoader, Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(Object contextObject) {
-        return new ResourceUtils(contextObject);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with itself as the context.
-     *
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create() {
-        return new ResourceUtils(null);
-    }
-
-    public ResourceUtils(ClassLoader loader, Object contextObject, String contextMessage) {
-        this(getClassLoadingContextInternal(loader, contextObject), contextObject, contextMessage);
-    }
-    
-    public ResourceUtils(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) {
-        this.loader = loader;
-        this.contextObject = contextObject;
-        this.context = contextMessage;
-    }
-
-    public ResourceUtils(Object contextObject, String contextMessage) {
-        this(contextObject==null ? null : getClassLoadingContextInternal(null, contextObject), contextObject, contextMessage);
-    }
-
-    public ResourceUtils(Object contextObject) {
-        this(contextObject, Strings.toString(contextObject));
-    }
-    
-    /** used to register custom mechanisms for getting classloaders given an object */
-    public static void addClassLoaderProvider(Function<Object,BrooklynClassLoadingContext> provider) {
-        classLoaderProviders.add(provider);
-    }
-    
-    // TODO rework this class so it accepts but does not require a BCLC ?
-    @SuppressWarnings("deprecation")
-    protected static BrooklynClassLoadingContext getClassLoadingContextInternal(ClassLoader loader, Object contextObject) {
-        if (contextObject instanceof BrooklynClassLoadingContext)
-            return (BrooklynClassLoadingContext) contextObject;
-        
-        for (Function<Object,BrooklynClassLoadingContext> provider: classLoaderProviders) {
-            BrooklynClassLoadingContext result = provider.apply(contextObject);
-            if (result!=null) return result;
-        }
-
-        BrooklynClassLoadingContext bl = BrooklynLoaderTracker.getLoader();
-        ManagementContext mgmt = (bl!=null ? bl.getManagementContext() : null);
-
-        ClassLoader cl = loader;
-        if (cl==null) cl = contextObject instanceof Class ? ((Class<?>)contextObject).getClassLoader() : 
-            contextObject instanceof ClassLoader ? ((ClassLoader)contextObject) : 
-                contextObject.getClass().getClassLoader();
-            
-        return JavaBrooklynClassLoadingContext.create(mgmt, cl);
-    }
-    
-    /** This should not be exposed as it risks it leaking into places where it would be serialized.
-     * Better for callers use {@link CatalogUtils#getClassLoadingContext(org.apache.brooklyn.api.entity.Entity)} or similar. }.
-     */
-    private BrooklynClassLoadingContext getLoader() {
-        return (loader!=null ? loader : getClassLoadingContextInternal(null, contextObject!=null ? contextObject : this));
-    }
-
-    /**
-     * @return all resources in Brooklyn's {@link BrooklynClassLoadingContext} with the given name.
-     */
-    public Iterable<URL> getResources(String name) {
-        return getLoader().getResources(name);
-    }
-    
-    /**
-     * Takes a string which is treated as a URL (with some extended "schemes" also expected),
-     * or as a path to something either on the classpath (absolute only) or the local filesystem (relative or absolute, depending on leading slash)
-     * <p>
-     * URLs can be of the form <b>classpath://com/acme/Foo.properties</b>
-     * as well as <b>file:///home/...</b> and <b>http://acme.com/...</b>.
-     * <p>
-     * Throws exception if not found, using the context parameter passed into the constructor.
-     * <p>
-     * TODO may want OSGi, or typed object; should consider pax url
-     * 
-     * @return a stream, or throws exception (never returns null)
-     */
-    public InputStream getResourceFromUrl(String url) {
-        try {
-            if (url==null) throw new NullPointerException("Cannot read from null");
-            if (url=="") throw new NullPointerException("Cannot read from empty string");
-            String orig = url;
-            String protocol = Urls.getProtocol(url);
-            if (protocol!=null) {
-                if ("classpath".equals(protocol)) {
-                    try {
-                        return getResourceViaClasspath(url);
-                    } catch (IOException e) {
-                        //catch the above because both orig and modified url may be interesting
-                        throw new IOException("Error accessing "+orig+": "+e, e);
-                    }
-                }
-                if ("sftp".equals(protocol)) {
-                    try {
-                        return getResourceViaSftp(url);
-                    } catch (IOException e) {
-                        throw new IOException("Error accessing "+orig+": "+e, e);
-                    }
-                }
-
-                if ("file".equals(protocol))
-                    url = tidyFileUrl(url);
-                
-                if ("data".equals(protocol)) {
-                    return new DataUriSchemeParser(url).lax().parse().getDataAsInputStream();
-                }
-
-                if ("http".equals(protocol) || "https".equals(protocol)) {
-                    return getResourceViaHttp(url);
-                }
-
-                return new URL(url).openStream();
-            }
-
-            try {
-                //try as classpath reference, then as file
-                try {
-                    URL u = getLoader().getResource(url);
-                    if (u!=null) return u.openStream();
-                } catch (IllegalArgumentException e) {
-                    //Felix installs an additional URL to the system classloader
-                    //which throws an IllegalArgumentException when passed a
-                    //windows path. See ExtensionManager.java static initializer.
-
-                    //ignore, not a classpath resource
-                }
-                if (url.startsWith("/")) {
-                    //some getResource calls fail if argument starts with /
-                    String urlNoSlash = url;
-                    while (urlNoSlash.startsWith("/")) urlNoSlash = urlNoSlash.substring(1);
-                    URL u = getLoader().getResource(urlNoSlash);
-                    if (u!=null) return u.openStream();
-//                    //Class.getResource can require a /  (else it attempts to be relative) but Class.getClassLoader doesn't
-//                    u = getLoader().getResource("/"+urlNoSlash);
-//                    if (u!=null) return u.openStream();
-                }
-                File f;
-                // but first, if it starts with tilde, treat specially
-                if (url.startsWith("~/")) {
-                    f = new File(Os.home(), url.substring(2));
-                } else if (url.startsWith("~\\")) {
-                    f = new File(Os.home(), url.substring(2));
-                } else {
-                    f = new File(url);
-                }
-                if (f.exists()) return new FileInputStream(f);
-            } catch (IOException e) {
-                //catch the above because both u and modified url will be interesting
-                throw new IOException("Error accessing "+orig+": "+e, e);
-            }
-            throw new IOException("'"+orig+"' not found on classpath or filesystem");
-        } catch (Exception e) {
-            if (context!=null) {
-                throw new RuntimeException("Error getting resource '"+url+"' for "+context+": "+e, e);
-            } else {
-                throw Exceptions.propagate(e);
-            }
-        }
-    }
-    
-    private final static Pattern pattern = Pattern.compile("^file:/*~/+(.*)$");
-
-    public static URL tidy(URL url) {
-        // File class has helpful methods for URIs but not URLs. So we convert.
-        URI in;
-        try {
-            in = url.toURI();
-        } catch (URISyntaxException e) {
-            throw Exceptions.propagate(e);
-        }
-        URI out;
-
-        Matcher matcher = pattern.matcher(in.toString());
-        if (matcher.matches()) {
-            // home-relative
-            File home = new File(Os.home());
-            File file = new File(home, matcher.group(1));
-            out = file.toURI();
-        } else if (in.getScheme().equals("file:")) {
-            // some other file, so canonicalize
-            File file = new File(in);
-            out = file.toURI();
-        } else {
-            // some other scheme, so no-op
-            out = in;
-        }
-
-        URL urlOut;
-        try {
-            urlOut = out.toURL();
-        } catch (MalformedURLException e) {
-            throw Exceptions.propagate(e);
-        }
-        if (!urlOut.equals(url) && log.isDebugEnabled()) {
-            log.debug("quietly changing " + url + " to " + urlOut);
-        }
-        return urlOut;
-    }
-    
-    public static String tidyFileUrl(String url) {
-        try {
-            return tidy(new URL(url)).toString();
-        } catch (MalformedURLException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    /** @deprecated since 0.7.0; use method {@link Os#mergePaths(String...)} */ @Deprecated
-    public static String mergeFilePaths(String... items) {
-        return Os.mergePaths(items);
-    }
-    
-    /** @deprecated since 0.7.0; use method {@link Os#tidyPath(String)} */ @Deprecated
-    public static String tidyFilePath(String path) {
-        return Os.tidyPath(path);
-    }
-    
-    /** @deprecated since 0.7.0; use method {@link Urls#getProtocol(String)} */ @Deprecated
-    public static String getProtocol(String url) {
-        return Urls.getProtocol(url);
-    }
-    
-    private InputStream getResourceViaClasspath(String url) throws IOException {
-        assert url.startsWith("classpath:");
-        String subUrl = url.substring("classpath:".length());
-        while (subUrl.startsWith("/")) subUrl = subUrl.substring(1);
-        URL u = getLoader().getResource(subUrl);
-        if (u!=null) return u.openStream();
-        else throw new IOException(subUrl+" not found on classpath");
-    }
-    
-    private InputStream getResourceViaSftp(String url) throws IOException {
-        assert url.startsWith("sftp://");
-        String subUrl = url.substring("sftp://".length());
-        String user;
-        String address;
-        String path;
-        int atIndex = subUrl.indexOf("@");
-        int colonIndex = subUrl.indexOf(":", (atIndex > 0 ? atIndex : 0));
-        if (colonIndex <= 0 || colonIndex <= atIndex) {
-            throw new IllegalArgumentException("Invalid sftp url ("+url+"); IP or hostname must be specified, such as sftp://localhost:/path/to/file");
-        }
-        if (subUrl.length() <= (colonIndex+1)) {
-            throw new IllegalArgumentException("Invalid sftp url ("+url+"); must specify path of remote file, such as sftp://localhost:/path/to/file");
-        }
-        if (atIndex >= 0) {
-            user = subUrl.substring(0, atIndex);
-        } else {
-            user = null;
-        }
-        address = subUrl.substring(atIndex + 1, colonIndex);
-        path = subUrl.substring(colonIndex+1);
-        
-        // TODO messy way to get an SCP session 
-        SshMachineLocation machine = new SshMachineLocation(MutableMap.builder()
-                .putIfNotNull("user", user)
-                .put("address", InetAddress.getByName(address))
-                .build());
-        try {
-            final File tempFile = Os.newTempFile("brooklyn-sftp", "tmp");
-            tempFile.setReadable(true, true);
-            machine.copyFrom(path, tempFile.getAbsolutePath());
-            return new FileInputStream(tempFile) {
-                @Override
-                public void close() throws IOException {
-                    super.close();
-                    tempFile.delete();
-                }
-            };
-        } finally {
-            Streams.closeQuietly(machine);
-        }
-    }
-    
-    //For HTTP(S) targets use HttpClient so
-    //we can do authentication
-    private InputStream getResourceViaHttp(String resource) throws IOException {
-        URI uri = URI.create(resource);
-        HttpClientBuilder builder = HttpTool.httpClientBuilder()
-                .laxRedirect(true)
-                .uri(uri);
-        Credentials credentials = getUrlCredentials(uri.getRawUserInfo());
-        if (credentials != null) {
-            builder.credentials(credentials);
-        }
-        HttpClient client = builder.build();
-        HttpResponse result = client.execute(new HttpGet(resource));
-        int statusCode = result.getStatusLine().getStatusCode();
-        if (HttpTool.isStatusCodeHealthy(statusCode)) {
-            HttpEntity entity = result.getEntity();
-            if (entity != null) {
-                return entity.getContent();
-            } else {
-                return new ByteArrayInputStream(new byte[0]);
-            }
-        } else {
-            EntityUtils.consume(result.getEntity());
-            throw new IllegalStateException("Invalid response invoking " + resource + ": response code " + statusCode);
-        }
-    }
-
-    private Credentials getUrlCredentials(String userInfo) {
-        if (userInfo != null) {
-            String[] arr = userInfo.split(":");
-            String username;
-            String password = null;
-            if (arr.length == 1) {
-                username = urlDecode(arr[0]);
-            } else if (arr.length == 2) {
-                username = urlDecode(arr[0]);
-                password = urlDecode(arr[1]);
-            } else {
-                return null;
-            }
-            return new UsernamePasswordCredentials(username, password);
-        } else {
-            return null;
-        }
-    }
-
-    private String urlDecode(String str) {
-        try {
-            return URLDecoder.decode(str, "UTF-8");
-        } catch (UnsupportedEncodingException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    /** takes {@link #getResourceFromUrl(String)} and reads fully, into a string */
-    public String getResourceAsString(String url) {
-        try {
-            return readFullyString(getResourceFromUrl(url));
-        } catch (Exception e) {
-            log.debug("ResourceUtils got error reading "+url+(context==null?"":" "+context)+" (rethrowing): "+e);
-            throw Throwables.propagate(e);
-        }
-    }
-
-    /** allows failing-fast if URL cannot be read */
-    public String checkUrlExists(String url) {
-        return checkUrlExists(url, null);
-    }
-    
-    public String checkUrlExists(String url, String message) {
-        if (url==null) throw new NullPointerException("URL "+(message!=null ? message+" " : "")+"must not be null");
-        InputStream s;
-        try {
-            s = getResourceFromUrl(url);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            throw new IllegalArgumentException("Unable to access URL "+(message!=null ? message : "")+": "+url, e);
-        }
-        Streams.closeQuietly(s); 
-        return url;
-    }
-
-    /** tests whether the url exists, returning true or false */
-    public boolean doesUrlExist(String url) {
-        InputStream s = null;
-        try {
-            s = getResourceFromUrl(url);
-            return true;
-        } catch (Exception e) {
-            return false;
-        } finally {
-            Streams.closeQuietly(s);
-        }
-    }
-    
-    /** returns the first available URL */
-    public Optional<String> firstAvailableUrl(String ...urls) {
-        for (String url: urls) {
-            if (doesUrlExist(url)) return Optional.of(url);
-        }
-        return Optional.absent();
-    }
-    
-    /** returns the base directory or JAR from which the context is class-loaded, if possible;
-     * throws exception if not found */
-    public String getClassLoaderDir() {
-        if (contextObject==null) throw new IllegalArgumentException("No suitable context ("+context+") to auto-detect classloader dir");
-        Class<?> cc = contextObject instanceof Class ? (Class<?>)contextObject : contextObject.getClass();
-        return getClassLoaderDir(cc.getCanonicalName().replace('.', '/')+".class");
-    }
-    
-    public String getClassLoaderDir(String resourceInThatDir) {
-        resourceInThatDir = Strings.removeFromStart(resourceInThatDir, "/");
-        URL resourceUrl = getLoader().getResource(resourceInThatDir);
-        if (resourceUrl==null) throw new NoSuchElementException("Resource ("+resourceInThatDir+") not found");
-
-        URL containerUrl = getContainerUrl(resourceUrl, resourceInThatDir);
-
-        if (!"file".equals(containerUrl.getProtocol())) throw new IllegalStateException("Resource ("+resourceInThatDir+") not on file system (at "+containerUrl+")");
-
-        //convert from file: URL to File
-        File file;
-        try {
-            file = new File(containerUrl.toURI());
-        } catch (URISyntaxException e) {
-            throw new IllegalStateException("Resource ("+resourceInThatDir+") found at invalid URI (" + containerUrl + ")", e);
-        }
-        
-        if (!file.exists()) throw new IllegalStateException("Context class url substring ("+containerUrl+") not found on filesystem");
-        return file.getPath();
-        
-    }
-
-    public static URL getContainerUrl(URL url, String resourceInThatDir) {
-        //Switching from manual parsing of jar: and file: URLs to java provided functionality.
-        //The old code was breaking on any Windows path and instead of fixing it, using
-        //the provided Java APIs seemed like the better option since they are already tested
-        //on multiple platforms.
-        boolean isJar = "jar".equals(url.getProtocol());
-        if(isJar) {
-            try {
-                //let java handle the parsing of jar URL, no network connection is established.
-                //Strips the jar protocol:
-                //  jar:file:/<path to jar>!<resourceInThatDir>
-                //  becomes
-                //  file:/<path to jar>
-                JarURLConnection connection = (JarURLConnection) url.openConnection();
-                url = connection.getJarFileURL();
-            } catch (IOException e) {
-                throw new IllegalStateException(e);
-            }
-        } else {
-            //Remove the trailing resouceInThatDir path from the URL, thus getting the parent folder.
-            String path = url.toString();
-            int i = path.indexOf(resourceInThatDir);
-            if (i==-1) throw new IllegalStateException("Resource path ("+resourceInThatDir+") not in url substring ("+url+")");
-            String parent = path.substring(0, i);
-            try {
-                url = new URL(parent);
-            } catch (MalformedURLException e) {
-                throw new IllegalStateException("Resource ("+resourceInThatDir+") found at invalid URL parent (" + parent + ")", e);
-            }
-        }
-        return url;
-    }
-    
-    /** @deprecated since 0.7.0 use {@link Streams#readFullyString(InputStream) */ @Deprecated
-    public static String readFullyString(InputStream is) throws IOException {
-        return Streams.readFullyString(is);
-    }
-
-    /** @deprecated since 0.7.0 use {@link Streams#readFully(InputStream) */ @Deprecated
-    public static byte[] readFullyBytes(InputStream is) throws IOException {
-        return Streams.readFully(is);
-    }
-    
-    /** @deprecated since 0.7.0 use {@link Streams#copy(InputStream, OutputStream)} */ @Deprecated
-    public static void copy(InputStream input, OutputStream output) throws IOException {
-        Streams.copy(input, output);
-    }
-
-    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
-    public static File mkdirs(File dir) {
-        return Os.mkdirs(dir);
-    }
-
-    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
-    public static File writeToTempFile(InputStream is, String prefix, String suffix) {
-        return Os.writeToTempFile(is, prefix, suffix);
-    }
-    
-    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
-    public static File writeToTempFile(InputStream is, File tempDir, String prefix, String suffix) {
-        return Os.writeToTempFile(is, tempDir, prefix, suffix);
-    }
-
-    /** @deprecated since 0.7.0; use method {@link Os#writePropertiesToTempFile(Properties, String, String)} */ @Deprecated
-    public static File writeToTempFile(Properties props, String prefix, String suffix) {
-        return Os.writePropertiesToTempFile(props, prefix, suffix);
-    }
-    
-    /** @deprecated since 0.7.0; use method {@link Os#writePropertiesToTempFile(Properties, File, String, String)} */ @Deprecated
-    public static File writeToTempFile(Properties props, File tempDir, String prefix, String suffix) {
-        return Os.writePropertiesToTempFile(props, tempDir, prefix, suffix);
-    }
-
-    /** @deprecated since 0.7.0; use method {@link Threads#addShutdownHook(Runnable)} */ @Deprecated
-    public static Thread addShutdownHook(final Runnable task) {
-        return Threads.addShutdownHook(task);
-    }
-    /** @deprecated since 0.7.0; use method {@link Threads#removeShutdownHook(Thread)} */ @Deprecated
-    public static boolean removeShutdownHook(Thread hook) {
-        return Threads.removeShutdownHook(hook);
-    }
-
-    /** returns the items with exactly one "/" between items (whether or not the individual items start or end with /),
-     * except where character before the / is a : (url syntax) in which case it will permit multiple (will not remove any) 
-     * @deprecated since 0.7.0 use either {@link Os#mergePathsUnix(String...)} {@link Urls#mergePaths(String...) */ @Deprecated
-    public static String mergePaths(String ...items) {
-        return Urls.mergePaths(items);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/config/ConfigBag.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/config/ConfigBag.java b/core/src/main/java/brooklyn/util/config/ConfigBag.java
deleted file mode 100644
index 2f17748..0000000
--- a/core/src/main/java/brooklyn/util/config/ConfigBag.java
+++ /dev/null
@@ -1,588 +0,0 @@
-/*
- * 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 brooklyn.util.config;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.ConcurrentModificationException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.javalang.JavaClassNames;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Objects;
-import com.google.common.collect.Sets;
-
-/**
- * Stores config in such a way that usage can be tracked.
- * Either {@link ConfigKey} or {@link String} keys can be inserted;
- * they will be stored internally as strings.
- * It is recommended to use {@link ConfigKey} instances to access,
- * although in some cases (such as setting fields from flags, or copying a map)
- * it may be necessary to mark things as used, or put, when only a string key is available.
- * <p>
- * This bag is order-preserving and thread-safe except where otherwise indicated,
- * currently by synching on this instance (but that behaviour may change).
- * <p>
- * @author alex
- */
-public class ConfigBag {
-
-    private static final Logger log = LoggerFactory.getLogger(ConfigBag.class);
-
-    /** an immutable, empty ConfigBag */
-    public static final ConfigBag EMPTY = new ConfigBag().setDescription("immutable empty config bag").seal();
-    
-    protected String description;
-    
-    private Map<String,Object> config;
-    private final Map<String,Object> unusedConfig;
-    private final boolean live;
-    private boolean sealed = false;
-
-    /** creates a new ConfigBag instance, empty and ready for population */
-    public static ConfigBag newInstance() {
-        return new ConfigBag();
-    }
-
-    /**
-     * Creates an instance that is backed by a "live map" (e.g. storage in a datagrid).
-     * The order-preserving nature of this class is only guaranteed if the
-     * provided storage has those properties. External modifications to the store can cause
-     * {@link ConcurrentModificationException} to be thrown, here or elsewhere. 
-     */
-    public static ConfigBag newLiveInstance(Map<String,Object> storage) {
-        return new ConfigBag(checkNotNull(storage, "storage map must be specified"));
-    }
-
-    public static ConfigBag newInstance(Map<?, ?> config) {
-        ConfigBag result = new ConfigBag();
-        result.putAll(config);
-        return result;
-    }
-
-    /** creates a new ConfigBag instance which includes all of the supplied ConfigBag's values,
-     * but which tracks usage separately (already used values are marked as such,
-     * but uses in the original set will not be marked here, and vice versa) */
-    public static ConfigBag newInstanceCopying(final ConfigBag configBag) {
-        return new ConfigBag().copy(configBag).setDescription(configBag.getDescription());
-    }
-    
-    /** creates a new ConfigBag instance which includes all of the supplied ConfigBag's values,
-     * plus an additional set of &lt;ConfigKey,Object&gt; or &lt;String,Object&gt; pairs
-     * <p>
-     * values from the original set which are used here will be marked as used in the original set
-     * (note: this applies even for values which are overridden and the overridden value is used);
-     * however subsequent uses in the original set will not be marked here
-     */
-    @Beta
-    public static ConfigBag newInstanceExtending(final ConfigBag parentBag) {
-        return new ConfigBagExtendingParent(parentBag);
-    }
-
-    /** @see #newInstanceExtending(ConfigBag) */
-    private static class ConfigBagExtendingParent extends ConfigBag {
-        ConfigBag parentBag;
-        private ConfigBagExtendingParent(ConfigBag parentBag) {
-            this.parentBag = parentBag;
-            copy(parentBag);
-        }
-        @Override
-        public void markUsed(String key) {
-            super.markUsed(key);
-            if (parentBag!=null)
-                parentBag.markUsed(key);
-        }
-    }
-    
-    /** As {@link #newInstanceExtending(ConfigBag)} but also putting the supplied values. */
-    @Beta
-    public static ConfigBag newInstanceExtending(final ConfigBag configBag, Map<?,?> optionalAdditionalValues) {
-        return newInstanceExtending(configBag).putAll(optionalAdditionalValues);
-    }
-
-    /** @deprecated since 0.7.0, not used; kept only for rebind compatibility where the inner class is used 
-     * (now replaced by a static class above) */
-    @Beta @Deprecated
-    public static ConfigBag newInstanceWithInnerClass(final ConfigBag configBag, Map<?,?> optionalAdditionalValues) {
-        return new ConfigBag() {
-            @Override
-            public void markUsed(String key) {
-                super.markUsed(key);
-                configBag.markUsed(key);
-            }
-        }.copy(configBag).putAll(optionalAdditionalValues);
-    }
-
-    public ConfigBag() {
-        config = new LinkedHashMap<String,Object>();
-        unusedConfig = new LinkedHashMap<String,Object>();
-        live = false;
-    }
-    
-    private ConfigBag(Map<String,Object> storage) {
-        this.config = storage;
-        unusedConfig = new LinkedHashMap<String,Object>();
-        live = true;
-    }
-    
-    public ConfigBag setDescription(String description) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot set description to '"+description+"': this config bag has been sealed and is now immutable.");
-        this.description = description;
-        return this;
-    }
-    
-    /** optional description used to provide context for operations */
-    public String getDescription() {
-        return description;
-    }
-    
-    /** current values for all entries 
-     * @return non-modifiable map of strings to object */
-    public synchronized Map<String,Object> getAllConfig() {
-        return MutableMap.copyOf(config).asUnmodifiable();
-    }
-
-    /** current values for all entries in a map where the keys are converted to {@link ConfigKey} instances */
-    public synchronized Map<ConfigKey<?>, ?> getAllConfigAsConfigKeyMap() {
-        Map<ConfigKey<?>,Object> result = MutableMap.of();
-        for (Map.Entry<String,Object> entry: config.entrySet()) {
-            result.put(ConfigKeys.newConfigKey(Object.class, entry.getKey()), entry.getValue());
-        }
-        return result;
-    }
-
-    /** Returns the internal map containing the current values for all entries;
-     * for use where the caller wants to modify this directly and knows it is safe to do so 
-     * <p>
-     * Accesses to the returned map must be synchronized on this bag if the 
-     * thread-safe behaviour is required. */ 
-    public Map<String,Object> getAllConfigMutable() {
-        if (live) {
-            // TODO sealed no longer works as before, because `config` is the backing storage map.
-            // Therefore returning it is dangerous! Even if we were to replace our field with an immutable copy,
-            // the underlying datagrid's map would still be modifiable. We need a way to switch the returned
-            // value's behaviour to sealable (i.e. wrapping the returned map).
-            return (sealed) ? MutableMap.copyOf(config).asUnmodifiable() : config;
-        } else {
-            return config;
-        }
-    }
-
-    /** current values for all entries which have not yet been used 
-     * @return non-modifiable map of strings to object */
-    public synchronized Map<String,Object> getUnusedConfig() {
-        return MutableMap.copyOf(unusedConfig).asUnmodifiable();
-    }
-
-    /** Returns the internal map containing the current values for all entries which have not yet been used;
-     * for use where the caller wants to modify this directly and knows it is safe to do so 
-     * <p>
-     * Accesses to the returned map must be synchronized on this bag if the 
-     * thread-safe behaviour is required. */ 
-    public Map<String,Object> getUnusedConfigMutable() {
-        return unusedConfig;
-    }
-
-    public ConfigBag putAll(Map<?,?> addlConfig) {
-        if (addlConfig==null) return this;
-        for (Map.Entry<?,?> e: addlConfig.entrySet()) {
-            putAsStringKey(e.getKey(), e.getValue());
-        }
-        return this;
-    }
-    
-    public ConfigBag putAll(ConfigBag addlConfig) {
-        return putAll(addlConfig.getAllConfig());
-    }
-    
-    public <T> ConfigBag putIfAbsent(ConfigKey<T> key, T value) {
-        return putIfAbsent(MutableMap.of(key, value));
-    }
-
-    public ConfigBag putAsStringKeyIfAbsent(Object key, Object value) {
-        return putIfAbsent(MutableMap.of(key, value));
-    }
-
-    public synchronized ConfigBag putIfAbsent(Map<?, ?> propertiesToSet) {
-        if (propertiesToSet==null)
-            return this;
-        for (Map.Entry<?, ?> entry: propertiesToSet.entrySet()) {
-            Object key = entry.getKey();
-            if (key instanceof HasConfigKey<?>)
-                key = ((HasConfigKey<?>)key).getConfigKey();
-            if (key instanceof ConfigKey<?>) {
-                if (!containsKey((ConfigKey<?>)key))
-                    putAsStringKey(key, entry.getValue());
-            } else if (key instanceof String) {
-                if (!containsKey((String)key))
-                    putAsStringKey(key, entry.getValue());
-            } else {
-                logInvalidKey(key);
-            }
-        }
-        return this;
-    }
-
-    public ConfigBag putIfAbsent(ConfigBag addlConfig) {
-        return putIfAbsent(addlConfig.getAllConfig());
-    }
-
-
-    @SuppressWarnings("unchecked")
-    public <T> T put(ConfigKey<T> key, T value) {
-        return (T) putStringKey(key.getName(), value);
-    }
-    
-    public <T> ConfigBag putIfNotNull(ConfigKey<T> key, T value) {
-        if (value!=null) put(key, value);
-        return this;
-    }
-
-    public <T> ConfigBag putIfAbsentAndNotNull(ConfigKey<T> key, T value) {
-        if (value!=null) putIfAbsent(key, value);
-        return this;
-    }
-
-    /** as {@link #put(ConfigKey, Object)} but returning this ConfigBag for fluent-style coding */
-    public <T> ConfigBag configure(ConfigKey<T> key, T value) {
-        putStringKey(key.getName(), value);
-        return this;
-    }
-    
-    public <T> ConfigBag configureStringKey(String key, T value) {
-        putStringKey(key, value);
-        return this;
-    }
-    
-    protected synchronized void putAsStringKey(Object key, Object value) {
-        if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
-        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
-        if (key instanceof String) {
-            putStringKey((String)key, value);
-        } else {
-            logInvalidKey(key);
-        }
-    }
-
-    protected void logInvalidKey(Object key) {
-        String message = (key == null ? "Invalid key 'null'" : "Invalid key type "+key.getClass().getCanonicalName()+" ("+key+")") +
-                " being used for configuration, ignoring";
-        log.debug(message, new Throwable("Source of "+message));
-        log.warn(message);
-    }
-    
-    /** recommended to use {@link #put(ConfigKey, Object)} but there are times
-     * (e.g. when copying a map) where we want to put a string key directly 
-     */
-    public synchronized Object putStringKey(String key, Object value) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot insert "+key+"="+value+": this config bag has been sealed and is now immutable.");
-        boolean isNew = !config.containsKey(key);
-        boolean isUsed = !isNew && !unusedConfig.containsKey(key);
-        Object old = config.put(key, value);
-        if (!isUsed) 
-            unusedConfig.put(key, value);
-        //if (!isNew && !isUsed) log.debug("updating config value which has already been used");
-        return old;
-    }
-    public Object putStringKeyIfHasValue(String key, Maybe<?> value) {
-        if (value.isPresent())
-            return putStringKey(key, value.get());
-        return null;
-    }
-    public Object putStringKeyIfNotNull(String key, Object value) {
-        if (value!=null)
-            return putStringKey(key, value);
-        return null;
-    }
-
-    public boolean containsKey(HasConfigKey<?> key) {
-        return containsKey(key.getConfigKey());
-    }
-
-    public boolean containsKey(ConfigKey<?> key) {
-        return containsKey(key.getName());
-    }
-
-    public synchronized boolean containsKey(String key) {
-        return config.containsKey(key);
-    }
-
-    /** returns the value of this config key, falling back to its default (use containsKey to see whether it was contained);
-     * also marks it as having been used (use peek to prevent marking as used)
-     */
-    public <T> T get(ConfigKey<T> key) {
-        return get(key, true);
-    }
-
-    /** gets a value from a string-valued key or null; ConfigKey is preferred, but this is useful in some contexts (e.g. setting from flags) */
-    public Object getStringKey(String key) {
-        return getStringKeyMaybe(key).orNull();
-    }
-    /** gets a {@link Maybe}-wrapped value from a string-valued key; ConfigKey is preferred, but this is useful in some contexts (e.g. setting from flags) */
-    public @Nonnull Maybe<Object> getStringKeyMaybe(String key) {
-        return getStringKeyMaybe(key, true);
-    }
-
-    /** gets a {@link Maybe}-wrapped value from a key, inferring the type of that key (e.g. {@link ConfigKey} or {@link String}) */
-    @Beta
-    public Maybe<Object> getObjKeyMaybe(Object key) {
-        if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
-        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
-        if (key instanceof String) {
-            return getStringKeyMaybe((String)key, true);
-        } else {
-            logInvalidKey(key);
-            return Maybe.absent();
-        }
-    }
-
-    /** like get, but without marking it as used */
-    public <T> T peek(ConfigKey<T> key) {
-        return get(key, false);
-    }
-
-    /** returns the first key in the list for which a value is explicitly set, then defaulting to defaulting value of preferred key */
-    public synchronized <T> T getFirst(ConfigKey<T> preferredKey, ConfigKey<T> ...otherCurrentKeysInOrderOfPreference) {
-        if (containsKey(preferredKey)) 
-            return get(preferredKey);
-        for (ConfigKey<T> key: otherCurrentKeysInOrderOfPreference) {
-            if (containsKey(key)) 
-                return get(key);
-        }
-        return get(preferredKey);
-    }
-
-    /** convenience for @see #getWithDeprecation(ConfigKey[], ConfigKey...) */
-    public Object getWithDeprecation(ConfigKey<?> key, ConfigKey<?> ...deprecatedKeys) {
-        return getWithDeprecation(new ConfigKey[] { key }, deprecatedKeys);
-    }
-
-    /** returns the value for the first key in the list for which a value is set,
-     * warning if any of the deprecated keys have a value which is different to that set on the first set current key
-     * (including warning if a deprecated key has a value but no current key does) */
-    public synchronized Object getWithDeprecation(ConfigKey<?>[] currentKeysInOrderOfPreference, ConfigKey<?> ...deprecatedKeys) {
-        // Get preferred key (or null)
-        ConfigKey<?> preferredKeyProvidingValue = null;
-        Object result = null;
-        boolean found = false;
-        for (ConfigKey<?> key: currentKeysInOrderOfPreference) {
-            if (containsKey(key)) {
-                preferredKeyProvidingValue = key;
-                result = get(preferredKeyProvidingValue);
-                found = true;
-                break;
-            }
-        }
-        
-        // Check if any deprecated keys are set
-        ConfigKey<?> deprecatedKeyProvidingValue = null;
-        Object deprecatedResult = null;
-        boolean foundDeprecated = false;
-        for (ConfigKey<?> deprecatedKey: deprecatedKeys) {
-            Object x = null;
-            boolean foundX = false;
-            if (containsKey(deprecatedKey)) {
-                x = get(deprecatedKey);
-                foundX = true;
-            }
-            if (foundX) {
-                if (found) {
-                    if (!Objects.equal(result, x)) {
-                        log.warn("Conflicting value from deprecated key " +deprecatedKey+", value "+x+
-                                "; using preferred key "+preferredKeyProvidingValue+" value "+result);
-                    } else {
-                        log.info("Deprecated key " +deprecatedKey+" ignored; has same value as preferred key "+preferredKeyProvidingValue+" ("+result+")");
-                    }
-                } else if (foundDeprecated) {
-                    if (!Objects.equal(result, x)) {
-                        log.warn("Conflicting values from deprecated keys: using " +deprecatedKeyProvidingValue+" instead of "+deprecatedKey+
-                                " (value "+deprecatedResult+" instead of "+x+")");
-                    } else {
-                        log.info("Deprecated key " +deprecatedKey+" ignored; has same value as other deprecated key "+preferredKeyProvidingValue+" ("+deprecatedResult+")");
-                    }
-                } else {
-                    // new value, from deprecated key
-                    log.warn("Deprecated key " +deprecatedKey+" detected (supplying value "+x+"), "+
-                            "; recommend changing to preferred key '"+currentKeysInOrderOfPreference[0]+"'; this will not be supported in future versions");
-                    deprecatedResult = x;
-                    deprecatedKeyProvidingValue = deprecatedKey;
-                    foundDeprecated = true;
-                }
-            }
-        }
-        
-        if (found) {
-            return result;
-        } else if (foundDeprecated) {
-            return deprecatedResult;
-        } else {
-            return currentKeysInOrderOfPreference[0].getDefaultValue();
-        }
-    }
-
-    protected <T> T get(ConfigKey<T> key, boolean markUsed) {
-        // TODO for now, no evaluation -- maps / closure content / other smart (self-extracting) keys are NOT supported
-        // (need a clean way to inject that behaviour, as well as desired TypeCoercions)
-        // this method, and the coercion, is not synchronized, nor does it need to be, because the "get" is synchronized. 
-        return coerceFirstNonNullKeyValue(key, getStringKey(key.getName(), markUsed));
-    }
-
-    /** returns the first non-null value to be the type indicated by the key, or the keys default value if no non-null values are supplied */
-    public static <T> T coerceFirstNonNullKeyValue(ConfigKey<T> key, Object ...values) {
-        for (Object o: values)
-            if (o!=null) return TypeCoercions.coerce(o, key.getTypeToken());
-        return TypeCoercions.coerce(key.getDefaultValue(), key.getTypeToken());
-    }
-
-    protected Object getStringKey(String key, boolean markUsed) {
-        return getStringKeyMaybe(key, markUsed).orNull();
-    }
-    protected synchronized Maybe<Object> getStringKeyMaybe(String key, boolean markUsed) {
-        if (config.containsKey(key)) {
-            if (markUsed) markUsed(key);
-            return Maybe.of(config.get(key));
-        }
-        return Maybe.absent();
-    }
-
-    /** indicates that a string key in the config map has been accessed */
-    public synchronized void markUsed(String key) {
-        unusedConfig.remove(key);
-    }
-
-    public synchronized void clear() {
-        if (sealed) 
-            throw new IllegalStateException("Cannot clear this config bag has been sealed and is now immutable.");
-        config.clear();
-        unusedConfig.clear();
-    }
-    
-    public ConfigBag removeAll(ConfigKey<?> ...keys) {
-        for (ConfigKey<?> key: keys) remove(key);
-        return this;
-    }
-
-    public synchronized void remove(ConfigKey<?> key) {
-        remove(key.getName());
-    }
-
-    public ConfigBag removeAll(Iterable<String> keys) {
-        for (String key: keys) remove(key);
-        return this;
-    }
-
-    public synchronized void remove(String key) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot remove "+key+": this config bag has been sealed and is now immutable.");
-        config.remove(key);
-        unusedConfig.remove(key);
-    }
-
-    public ConfigBag copy(ConfigBag other) {
-        // ensure locks are taken in a canonical order to prevent deadlock
-        if (other==null) {
-            synchronized (this) {
-                return copyWhileSynched(other);
-            }
-        }
-        if (System.identityHashCode(other) < System.identityHashCode(this)) {
-            synchronized (other) {
-                synchronized (this) {
-                    return copyWhileSynched(other);
-                }
-            }
-        } else {
-            synchronized (this) {
-                synchronized (other) {
-                    return copyWhileSynched(other);
-                }
-            }
-        }
-    }
-    
-    protected ConfigBag copyWhileSynched(ConfigBag other) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot copy "+other+" to "+this+": this config bag has been sealed and is now immutable.");
-        putAll(other.getAllConfig());
-        markAll(Sets.difference(other.getAllConfig().keySet(), other.getUnusedConfig().keySet()));
-        setDescription(other.getDescription());
-        return this;
-    }
-
-    public synchronized int size() {
-        return config.size();
-    }
-    
-    public synchronized boolean isEmpty() {
-        return config.isEmpty();
-    }
-    
-    public ConfigBag markAll(Iterable<String> usedFlags) {
-        for (String flag: usedFlags)
-            markUsed(flag);
-        return this;
-    }
-
-    public synchronized boolean isUnused(ConfigKey<?> key) {
-        return unusedConfig.containsKey(key.getName());
-    }
-    
-    /** makes this config bag immutable; any attempts to change subsequently 
-     * (apart from marking fields as used) will throw an exception
-     * <p>
-     * copies will be unsealed however
-     * <p>
-     * returns this for convenience (fluent usage) */
-    public ConfigBag seal() {
-        sealed = true;
-        if (live) {
-            // TODO How to ensure sealed?!
-        } else {
-            config = getAllConfig();
-        }
-        return this;
-    }
-
-    // TODO why have both this and mutable
-    /** @see #getAllConfigMutable() */
-    public Map<String, Object> getAllConfigRaw() {
-        return getAllConfigMutable();
-    }
-    
-    @Override
-    public String toString() {
-        return JavaClassNames.simpleClassName(this)+"["+getAllConfigRaw()+"]";
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java b/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
deleted file mode 100644
index a1bc125..0000000
--- a/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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 brooklyn.util.crypto;
-
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.jce.X509Principal;
-
-import brooklyn.util.exceptions.Exceptions;
-
-/** A fluent API which simplifies generating certificates (signed keys) */
-/* NB - re deprecation - we use deprecated X509V3CertificateGenerator still
- * because the official replacement, X509v3CertificateBuilder, 
- * drags in an add'l dependency (bcmail) and is harder to use. */
-public class FluentKeySigner {
-
-    static { BrooklynInitialization.initSecureKeysBouncyCastleProvider(); }
-
-    protected X500Principal issuerPrincipal;
-    protected KeyPair issuerKey;
-
-    protected SecureRandom srand = new SecureRandom();
-    
-    protected Date validityStartDate, validityEndDate;
-    protected BigInteger serialNumber;
-    
-    protected String signatureAlgorithm = "MD5WithRSAEncryption";
-    protected AuthorityKeyIdentifier authorityKeyIdentifier;
-    protected X509Certificate authorityCertificate;
-
-    public FluentKeySigner(X500Principal issuerPrincipal, KeyPair issuerKey) {
-        this.issuerPrincipal = issuerPrincipal;
-        this.issuerKey = issuerKey;
-        validFromDaysAgo(7);
-        validForYears(10);
-    }
-    public FluentKeySigner(String issuerCommonName, KeyPair issuerKey) {
-        this(SecureKeys.getX500PrincipalWithCommonName(issuerCommonName), issuerKey);
-    }
-    
-    public FluentKeySigner(String issuerCommonName) {
-        this(issuerCommonName, SecureKeys.newKeyPair());
-    }
-
-    public FluentKeySigner(X509Certificate caCert, KeyPair caKey) {
-        this(caCert.getIssuerX500Principal(), caKey);
-        authorityCertificate(caCert);
-    }
-    
-    public KeyPair getKey() {
-        return issuerKey;
-    }
-    
-    public X500Principal getPrincipal() {
-        return issuerPrincipal;
-    }
-    
-    @SuppressWarnings("deprecation")
-    public String getCommonName() {
-//        TODO see deprecation note at top of file
-        // for modernising, would RFC4519Style.cn work ?
-        return (String) new X509Principal(issuerPrincipal.getName()).getValues(org.bouncycastle.asn1.x509.X509Name.CN).elementAt(0);
-    }
-    
-    public X509Certificate getAuthorityCertificate() {
-        return authorityCertificate;
-    }
-    
-    public FluentKeySigner validFromDaysAgo(long days) {
-        return validFrom(new Date( (System.currentTimeMillis() / (1000L*60*60*24) - days) * 1000L*60*60*24));            
-    }
-
-    public FluentKeySigner validFrom(Date d) {
-        validityStartDate = d;
-        return this;
-    }
-
-    public FluentKeySigner validForYears(long years) {
-        return validUntil(new Date( (System.currentTimeMillis() / (1000L*60*60*24) + 365*years) * 1000L*60*60*24));            
-    }
-
-    public FluentKeySigner validUntil(Date d) {
-        validityEndDate = d;
-        return this;
-    }
-
-    /** use a hard-coded serial number; or make one up, if null */
-    public FluentKeySigner serialNumber(BigInteger serialNumber) {
-        this.serialNumber = serialNumber;
-        return this;
-    }
-
-    public FluentKeySigner signatureAlgorithm(String signatureAlgorithm) {
-        this.signatureAlgorithm = signatureAlgorithm;
-        return this;
-    }
-
-    @SuppressWarnings("deprecation")
-    public FluentKeySigner authorityCertificate(X509Certificate certificate) {
-        try {
-            authorityKeyIdentifier(new org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure(certificate));
-            this.authorityCertificate = certificate;
-            return this;
-        } catch (CertificateParsingException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    public FluentKeySigner authorityKeyIdentifier(AuthorityKeyIdentifier authorityKeyIdentifier) {
-        this.authorityKeyIdentifier = authorityKeyIdentifier;
-        return this;
-    }
-    
-    public FluentKeySigner selfsign() {
-        if (authorityCertificate!=null) throw new IllegalStateException("Signer already has certificate");
-        authorityCertificate(newCertificateFor(getCommonName(), getKey()));
-        return this;
-    }
-
-    // TODO see note re deprecation at start of file
-    @SuppressWarnings("deprecation")
-    public X509Certificate newCertificateFor(X500Principal subject, PublicKey keyToCertify) {
-        try {
-            org.bouncycastle.x509.X509V3CertificateGenerator v3CertGen = new org.bouncycastle.x509.X509V3CertificateGenerator();
-
-            v3CertGen.setSerialNumber(
-                    serialNumber != null ? serialNumber :
-                        // must be positive
-                        BigInteger.valueOf(srand.nextLong()).abs().add(BigInteger.ONE));  
-            v3CertGen.setIssuerDN(issuerPrincipal);  
-            v3CertGen.setNotBefore(validityStartDate);  
-            v3CertGen.setNotAfter(validityEndDate);
-            v3CertGen.setSignatureAlgorithm(signatureAlgorithm);   
-
-            v3CertGen.setSubjectDN(subject);  
-            v3CertGen.setPublicKey(keyToCertify);  
-
-            v3CertGen.addExtension(X509Extension.subjectKeyIdentifier, false,
-                    new org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure(keyToCertify));
-
-            if (authorityKeyIdentifier!=null)
-                v3CertGen.addExtension(X509Extension.authorityKeyIdentifier, false,
-                        authorityKeyIdentifier);
-
-            X509Certificate pkCertificate = v3CertGen.generate(issuerKey.getPrivate(), "BC");
-            return pkCertificate;
-            
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    public X509Certificate newCertificateFor(String commonName, PublicKey key) {
-//        SecureKeys.getX509PrincipalWithCommonName(commonName)
-        return newCertificateFor(
-                SecureKeys.getX500PrincipalWithCommonName(commonName)
-//                new X509Principal("CN=" + commonName + ", OU=None, O=None, L=None, C=None")
-                , key);
-    }
-
-    public X509Certificate newCertificateFor(String commonName, KeyPair key) {
-        return newCertificateFor(commonName, key.getPublic());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/crypto/SecureKeys.java b/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
deleted file mode 100644
index 29fcf32..0000000
--- a/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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 brooklyn.util.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Security;
-
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMDecryptorProvider;
-import org.bouncycastle.openssl.PEMEncryptedKeyPair;
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.PEMWriter;
-import org.bouncycastle.openssl.PasswordFinder;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.stream.Streams;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Throwables;
-
-/**
- * Utility methods for generating and working with keys,
- * extending the parent class with useful things provided by BouncyCastle crypto library.
- * (Parent class is in a different project where BC is not included as a dependency.)
- */
-public class SecureKeys extends SecureKeysWithoutBouncyCastle {
-
-    private static final Logger log = LoggerFactory.getLogger(SecureKeys.class);
-    
-    static { BrooklynInitialization.initSecureKeysBouncyCastleProvider(); }
-    
-    public static void initBouncyCastleProvider() {
-        Security.addProvider(new BouncyCastleProvider());
-    }
-    
-    public static class PassphraseProblem extends IllegalStateException {
-        private static final long serialVersionUID = -3382824813899223447L;
-        public PassphraseProblem(String message) { super("Passphrase problem with this key: "+message); }
-        public PassphraseProblem(String message, Exception cause) { super("Passphrase problem with this key: "+message, cause); }
-    }
-    
-    private SecureKeys() {}
-    
-    /** RFC1773 order, with None for other values. Normally prefer X500Principal. */
-    public static X509Principal getX509PrincipalWithCommonName(String commonName) {
-        return new X509Principal("" + "C=None," + "L=None," + "O=None," + "OU=None," + "CN=" + commonName);
-    }
-
-    /** reads RSA or DSA / pem style private key files (viz {@link #toPem(KeyPair)}), extracting also the public key if possible
-     * @throws IllegalStateException on errors, in particular {@link PassphraseProblem} if that is the problem */
-    public static KeyPair readPem(InputStream input, final String passphrase) {
-        // TODO cache is only for fallback "reader" strategy (2015-01); delete when Parser confirmed working
-        byte[] cache = Streams.readFully(input);
-        input = new ByteArrayInputStream(cache);
-
-        try {
-            PEMParser pemParser = new PEMParser(new InputStreamReader(input));
-
-            Object object = pemParser.readObject();
-            pemParser.close();
-
-            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
-            KeyPair kp = null;
-            if (object==null) {
-                throw new IllegalStateException("PEM parsing failed: missing or invalid data");
-            } else if (object instanceof PEMEncryptedKeyPair) {
-                if (passphrase==null) throw new PassphraseProblem("passphrase required");
-                try {
-                    PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(passphrase.toCharArray());
-                    kp = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    throw new PassphraseProblem("wrong passphrase", e);
-                }
-            } else  if (object instanceof PEMKeyPair) {
-                kp = converter.getKeyPair((PEMKeyPair) object);
-            } else if (object instanceof PrivateKeyInfo) {
-                PrivateKey privKey = converter.getPrivateKey((PrivateKeyInfo) object);
-                kp = new KeyPair(null, privKey);
-            } else {
-                throw new IllegalStateException("PEM parser support missing for: "+object);
-            }
-
-            return kp;
-
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-
-            // older code relied on PEMReader, now deprecated
-            // replaced with above based on http://stackoverflow.com/questions/14919048/bouncy-castle-pemreader-pemparser
-            // passes the same tests (Jan 2015) but leaving the old code as a fallback for the time being 
-
-            input = new ByteArrayInputStream(cache);
-            try {
-                Security.addProvider(new BouncyCastleProvider());
-                @SuppressWarnings("deprecation")
-                org.bouncycastle.openssl.PEMReader pr = new org.bouncycastle.openssl.PEMReader(new InputStreamReader(input), new PasswordFinder() {
-                    public char[] getPassword() {
-                        return passphrase!=null ? passphrase.toCharArray() : new char[0];
-                    }
-                });
-                @SuppressWarnings("deprecation")
-                KeyPair result = (KeyPair) pr.readObject();
-                pr.close();
-                if (result==null)
-                    throw Exceptions.propagate(e);
-                
-                log.warn("PEMParser failed when deprecated PEMReader succeeded, with "+result+"; had: "+e);
-
-                return result;
-
-            } catch (Exception e2) {
-                Exceptions.propagateIfFatal(e2);
-                throw Exceptions.propagate(e);
-            }
-        }
-    }
-
-    /** because KeyPair.equals is not implemented :( */
-    public static boolean equal(KeyPair k1, KeyPair k2) {
-        return Objects.equal(k2.getPrivate(), k1.getPrivate()) && Objects.equal(k2.getPublic(), k1.getPublic());
-    }
-
-    /** returns the PEM (base64, ie for id_rsa) string for the private key / key pair;
-     * this starts -----BEGIN PRIVATE KEY----- and ends similarly, like id_rsa.
-     * also see {@link #readPem(InputStream, String)} */
-    public static String toPem(KeyPair key) {
-        return stringPem(key);
-    }
-
-    /** returns id_rsa.pub style file, of public key */
-    public static String toPub(KeyPair key) {
-        return AuthorizedKeysParser.encodePublicKey(key.getPublic());
-    }
-    
-    /** opposite of {@link #toPub(KeyPair)}, given text */
-    public static PublicKey fromPub(String pubText) {
-        return AuthorizedKeysParser.decodePublicKey(pubText);
-    }
-
-    /** @deprecated since 0.7.0, use {@link #toPem(KeyPair)} */ @Deprecated
-    public static String stringPem(KeyPair key) {
-        try {
-            StringWriter sw = new StringWriter();
-            PEMWriter w = new PEMWriter(sw);
-            w.writeObject(key);
-            w.close();
-            return sw.toString();
-        } catch (IOException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java b/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java
deleted file mode 100644
index 6985406..0000000
--- a/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * 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 brooklyn.util.file;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collections;
-import java.util.Map;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.zip.ZipOutputStream;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.file.ArchiveUtils.ArchiveType;
-import brooklyn.util.os.Os;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.io.Files;
-
-/**
- * Build a Zip or Jar archive.
- * <p>
- * Supports creating temporary archives that will be deleted on exit, if no name is
- * specified. The created file must be a Java archive type, with the extension {@code .zip},
- * {@code .jar}, {@code .war} or {@code .ear}.
- * <p>
- * Example:
- * <pre> File zip = ArchiveBuilder.archive("data/archive.zip")
- *         .addAt(new File("./pom.xml"), "")
- *         .addDirContentsAt(new File("./src"), "src/")
- *         .addAt(new File("/tmp/Extra.java"), "src/main/java/")
- *         .addDirContentsAt(new File("/tmp/overlay/"), "")
- *         .create();
- * </pre>
- * <p>
- */
-@Beta
-public class ArchiveBuilder {
-
-    /**
-     * Create an {@link ArchiveBuilder} for an archive with the given name.
-     */
-    public static ArchiveBuilder archive(String archive) {
-        return new ArchiveBuilder(archive);
-    }
-
-    /**
-     * Create an {@link ArchiveBuilder} for a {@link ArchiveType#ZIP Zip} format archive.
-     */
-    public static ArchiveBuilder zip() {
-        return new ArchiveBuilder(ArchiveType.ZIP);
-    }
-
-    /**
-     * Create an {@link ArchiveBuilder} for a {@link ArchiveType#JAR Jar} format archive.
-     */
-    public static ArchiveBuilder jar() {
-        return new ArchiveBuilder(ArchiveType.JAR);
-    }
-
-    // TODO would be nice to support TAR and TGZ
-    // e.g. using commons-compress
-    // TarArchiveOutputStream out = new TarArchiveOutputStream(new GZIPOutputStream(bytes));
-    // but I think the way entries are done is slightly different so we'd need a bit of refactoring
-    
-    private final ArchiveType type;
-    private File archive;
-    private Manifest manifest;
-    private Multimap<String, File> entries = LinkedHashMultimap.create();
-
-    private ArchiveBuilder() {
-        this(ArchiveType.ZIP);
-    }
-
-    private ArchiveBuilder(String filename) {
-        this(ArchiveType.of(filename));
-
-        named(filename);
-    }
-
-    private ArchiveBuilder(ArchiveType type) {
-        checkNotNull(type);
-        checkArgument(ArchiveType.ZIP_ARCHIVES.contains(type));
-
-        this.type = type;
-        this.manifest = new Manifest();
-    }
-
-    /**
-     * Set the location of the generated archive file.
-     */
-    public ArchiveBuilder named(String name) {
-        checkNotNull(name);
-        String ext = Files.getFileExtension(name);
-        if (ext.isEmpty()) {
-            name = name + "." + type.toString();
-        } else if (type != ArchiveType.of(name)) {
-            throw new IllegalArgumentException(String.format("Extension for '%s' did not match archive type of %s", ext, type));
-        }
-        this.archive = new File(Os.tidyPath(name));
-        return this;
-    }
-
-    /**
-     * @see #named(String)
-     */
-    public ArchiveBuilder named(File file) {
-        checkNotNull(file);
-        return named(file.getPath());
-    }
-
-    /**
-     * Add a manifest entry with the given {@code key} and {@code value}.
-     */
-    public ArchiveBuilder manifest(Object key, Object value) {
-        checkNotNull(key, "key");
-        checkNotNull(value, "value");
-        manifest.getMainAttributes().put(key, value);
-        return this;
-    }
-
-    /**
-     * Add the file located at the {@code filePath} to the archive, 
-     * with some complicated base-name strategies.
-     *
-     * @deprecated since 0.7.0 use one of the other add methods which makes the strategy explicit */ @Deprecated
-    public ArchiveBuilder add(String filePath) {
-        checkNotNull(filePath, "filePath");
-        return add(new File(Os.tidyPath(filePath)));
-    }
-
-    /**
-     * Add the {@code file} to the archive.
-     * <p>
-     * If the file path is absolute, or points to a file above the current directory,
-     * the file is added to the archive as a top-level entry, using the file name only.
-     * For relative {@code filePath}s below the current directory, the file is added
-     * using the path given and is assumed to be located relative to the current
-     * working directory.
-     * <p>
-     * No checks for file existence are made at this stage.
-     *
-     * @see #entry(String, File)
-     * @deprecated since 0.7.0 use one of the other add methods which makes the strategy explicit */ @Deprecated
-    public ArchiveBuilder add(File file) {
-        checkNotNull(file, "file");
-        String filePath = Os.tidyPath(file.getPath());
-        if (file.isAbsolute() || filePath.startsWith("../")) {
-            return entry(Os.mergePaths(".", file.getName()), file);
-        } else {
-            return entry(Os.mergePaths(".", filePath), file);
-        }
-    }
-
-    /**
-     * Add the file located at the {@code fileSubPath}, relative to the {@code baseDir} on the local system,
-     * to the archive.
-     * <p>
-     * Uses the {@code fileSubPath} as the name of the file in the archive. Note that the
-     * file is found by concatenating the two path components using {@link Os#mergePaths(String...)},
-     * thus {@code fileSubPath} should not be absolute or point to a location above the current directory.
-     * <p>
-     * Use {@link #entry(String, String)} directly or {@link #entries(Map)} for complete
-     * control over file locations and names in the archive.
-     *
-     * @see #entry(String, String)
-     */
-    public ArchiveBuilder addFromLocalBaseDir(File baseDir, String fileSubPath) {
-        checkNotNull(baseDir, "baseDir");
-        checkNotNull(fileSubPath, "filePath");
-        return entry(Os.mergePaths(".", fileSubPath), Os.mergePaths(baseDir.getPath(), fileSubPath));
-    }
-    /** @deprecated since 0.7.0 use {@link #addFromLocalBaseDir(File, String)}, or
-     * one of the other add methods if adding relative to baseDir was not intended */ @Deprecated
-    public ArchiveBuilder addFromLocalBaseDir(String baseDir, String fileSubPath) {
-        return addFromLocalBaseDir(new File(baseDir), fileSubPath);
-    }
-    /** @deprecated since 0.7.0 use {@link #addFromLocalBaseDir(File, String)}, or
-     * one of the other add methods if adding relative to baseDir was not intended */ @Deprecated
-    public ArchiveBuilder add(String baseDir, String fileSubPath) {
-        return addFromLocalBaseDir(baseDir, fileSubPath);
-    }
-     
-    /** adds the given file to the archive, preserving its name but putting under the given directory in the archive (may be <code>""</code> or <code>"./"</code>) */
-    public ArchiveBuilder addAt(File file, String archiveParentDir) {
-        checkNotNull(archiveParentDir, "archiveParentDir");
-        checkNotNull(file, "file");
-        return entry(Os.mergePaths(archiveParentDir, file.getName()), file);
-    }
-
-    /**
-     * Add the contents of the directory named {@code dirName} to the archive.
-     *
-     * @see #addDir(File)
-     * @deprecated since 0.7.0 use {@link #addDirContentsAt(File, String) */ @Deprecated
-    public ArchiveBuilder addDir(String dirName) {
-        checkNotNull(dirName, "dirName");
-        return addDir(new File(Os.tidyPath(dirName)));
-    }
-
-    /**
-     * Add the contents of the directory {@code dir} to the archive.
-     * The directory's name is not included; use {@link #addAtRoot(File)} if you want that behaviour. 
-     * <p>
-     * Uses {@literal .} as the parent directory name for the contents.
-     *
-     * @see #entry(String, File)
-     */
-    public ArchiveBuilder addDirContentsAt(File dir, String archiveParentDir) {
-        checkNotNull(dir, "dir");
-        if (!dir.isDirectory()) throw new IllegalArgumentException(dir+" is not a directory; cannot add contents to archive");
-        return entry(archiveParentDir, dir);
-    }
-    /**
-     * As {@link #addDirContentsAt(File, String)}, 
-     * using {@literal .} as the parent directory name for the contents.
-     * 
-     * @deprecated since 0.7.0 use {@link #addDirContentsAt(File, String)
-     * to clarify API, argument types, and be explicit about where it should be installed,
-     * because JARs seem to require <code>""<code> whereas ZIPs might want <code>"./"</code>. */ @Deprecated
-    public ArchiveBuilder addDir(File dir) {
-        return addDirContentsAt(dir, ".");
-    }
-
-    /**
-     * Add the collection of {@code files} to the archive.
-     *
-     * @see #add(String)
-     * @deprecated since 0.7.0 use one of the other add methods if keeping this file's path was not intended */ @Deprecated
-    public ArchiveBuilder add(Iterable<String> files) {
-        checkNotNull(files, "files");
-        for (String filePath : files) {
-            add(filePath);
-        }
-        return this;
-    }
-
-    /**
-     * Add the collection of {@code files}, relative to the {@code baseDir}, to
-     * the archive.
-     *
-     * @see #add(String, String)
-     * @deprecated since 0.7.0 use one of the other add methods if keeping this file's path was not intended */ @Deprecated
-    public ArchiveBuilder add(String baseDir, Iterable<String> files) {
-        checkNotNull(baseDir, "baseDir");
-        checkNotNull(files, "files");
-        for (String filePath : files) {
-            add(baseDir, filePath);
-        }
-        return this;
-    }
-
-    /**
-     * Add the {@code file} to the archive with the path {@code entryPath}.
-     *
-     * @see #entry(String, File)
-     */
-    public ArchiveBuilder entry(String entryPath, String filePath) {
-        checkNotNull(entryPath, "entryPath");
-        checkNotNull(filePath, "filePath");
-        return entry(entryPath, new File(filePath));
-    }
-
-    /**
-     * Add the {@code file} to the archive with the path {@code entryPath}.
-     */
-    public ArchiveBuilder entry(String entryPath, File file) {
-        checkNotNull(entryPath, "entryPath");
-        checkNotNull(file, "file");
-        this.entries.put(entryPath, file);
-        return this;
-    }
-
-    /**
-     * Add a {@link Map} of entries to the archive.
-     * <p>
-     * The keys should be the names of the file entries to be added to the archive and
-     * the value should point to the actual {@link File} to be added.
-     * <p>
-     * This allows complete control over the directory structure of the eventual archive,
-     * as the entry names do not need to bear any relationship to the name or location
-     * of the files on the filesystem.
-     */
-    public ArchiveBuilder entries(Map<String, File> entries) {
-        checkNotNull(entries, "entries");
-        for (Map.Entry<String, File> entry: entries.entrySet())
-            this.entries.put(entry.getKey(), entry.getValue());
-        return this;
-    }
-
-    /**
-     * Generates the archive and outputs it to the given stream, ignoring any file name.
-     * <p>
-     * This will add a manifest file if the type is a Jar archive.
-     */
-    public void stream(OutputStream output) {
-        try {
-            ZipOutputStream target;
-            if (type == ArchiveType.ZIP) {
-                target = new ZipOutputStream(output);
-            } else {
-                manifest(Attributes.Name.MANIFEST_VERSION, "1.0");
-                target = new JarOutputStream(output, manifest);
-            }
-            for (String entry : entries.keySet()) {
-                addToArchive(entry, entries.get(entry), target);
-            }
-            target.close();
-        } catch (IOException ioe) {
-            throw Exceptions.propagate(ioe);
-        }
-    }
-
-    /**
-     * Generates the archive, saving it with the given name.
-     */
-    public File create(String archiveFile) {
-        return named(archiveFile).create();
-    }
-
-    /**
-     * Generates the archive.
-     * <p>
-     * If no name has been specified, the archive will be created as a temporary file with
-     * a unique name, that is deleted on exit. Otherwise, the given name will be used.
-     */
-    public File create() {
-        if (archive == null) {
-            File temp = Os.newTempFile("brooklyn-archive", type.toString());
-            temp.deleteOnExit();
-            named(temp);
-        }
-        try {
-            OutputStream output = new FileOutputStream(archive);
-            stream(output);
-            output.close();
-        } catch (IOException ioe) {
-            throw Exceptions.propagate(ioe);
-        }
-        return archive;
-    }
-
-    /**
-     * Recursively add files to the archive.
-     * <p>
-     * Code adapted from this <a href="http://stackoverflow.com/questions/1281229/how-to-use-jaroutputstream-to-create-a-jar-file">example</a>
-     * <p>
-     * <strong>Note</strong> {@link File} provides no support for symbolic links, and as such there is
-     * no way to ensure that a symbolic link to a directory is not followed when traversing the
-     * tree. In this case, iterables created by this traverser could contain files that are
-     * outside of the given directory or even be infinite if there is a symbolic link loop.
-     */
-    private void addToArchive(String path, Iterable<File> sources, ZipOutputStream target) throws IOException {
-        int size = Iterables.size(sources);
-        if (size==0) return;
-        boolean isDirectory;
-        if (size>1) {
-            // it must be directories if we are putting multiple things here 
-            isDirectory = true;
-        } else {
-            isDirectory = Iterables.getOnlyElement(sources).isDirectory();
-        }
-        
-        String name = path.replace("\\", "/");
-        if (isDirectory) {
-            name += "/";
-            JarEntry entry = new JarEntry(name);
-            
-            long lastModified=-1;
-            for (File source: sources)
-                if (source.lastModified()>lastModified)
-                    lastModified = source.lastModified();
-            
-            entry.setTime(lastModified);
-            target.putNextEntry(entry);
-            target.closeEntry();
-
-            for (File source: sources) {
-                if (!source.isDirectory()) {
-                    throw new IllegalStateException("Cannot add multiple items at a path in archive unless they are directories: "+sources+" at "+path+" is not valid.");
-                }
-                Iterable<File> children = Files.fileTreeTraverser().children(source);
-                for (File child : children) {
-                    addToArchive(Os.mergePaths(path, child.getName()), Collections.singleton(child), target);
-                }
-            }
-            return;
-        }
-
-        File source = Iterables.getOnlyElement(sources);
-        JarEntry entry = new JarEntry(name);
-        entry.setTime(source.lastModified());
-        target.putNextEntry(entry);
-        Files.asByteSource(source).copyTo(target);
-        target.closeEntry();
-    }
-}