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 <ConfigKey,Object> or <String,Object> 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();
- }
-}