brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hadr...@apache.org
Subject [13/64] [abbrv] incubator-brooklyn git commit: BROOKLYN-162 - apply org.apache package prefix to utils-common
Date Tue, 18 Aug 2015 15:03:22 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenArtifact.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenArtifact.java b/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenArtifact.java
new file mode 100644
index 0000000..b9de2f1
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenArtifact.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.maven;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MavenArtifact {
+
+    private static final Logger log = LoggerFactory.getLogger(MavenArtifact.class);
+    
+    protected final @Nonnull String groupId; 
+    protected final @Nonnull String artifactId; 
+    protected final @Nonnull String packaging;
+    protected final @Nullable String classifier; 
+    protected final @Nonnull String version; 
+    
+    /** a custom marker inserted after the artifactId and before the version, offset by an additional "-";
+     * defaults to null (nothing)
+     * <p>
+     * uses: when a shaded JAR is built, sometimes the word shaded is inserted before the version
+     * (and the "with-dependencies" classifier overwritten) */
+    protected @Nullable String customFileNameAfterArtifactMarker;
+    
+    /** a custom marker inserted after the version and before the extension, offset by an additional "-" if non-empty;
+     * defaults to {@link #getClassifier()} if null, but can replace the classifier
+     * <p>
+     * uses: removing classifier by specifying "", or adding a notional classifier such as "dist" */
+    protected @Nullable String classifierFileNameMarker;
+    
+    public MavenArtifact(String groupId, String artifactId, String packaging, String classifier, String version) {
+        super();
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+        this.packaging = packaging;
+        this.classifier = classifier;
+        this.version = version;
+    }
+    
+    public MavenArtifact(String groupId, String artifactId, String packaging, String version) {
+        super();
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+        this.packaging = packaging;
+        this.classifier = null;
+        this.version = version;
+    }
+
+    public static MavenArtifact fromCoordinate(String coordinate) {
+        String[] parts = checkNotNull(coordinate, "coordinate").split(":");
+        if (parts.length==4)
+            return new MavenArtifact(parts[0], parts[1], parts[2], parts[3]);
+        if (parts.length==5)
+            return new MavenArtifact(parts[0], parts[1], parts[2], parts[3], parts[4]);
+        throw new IllegalArgumentException("Invalid maven coordinate '"+coordinate+"'");
+    }
+    
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    @Nullable public String getClassifier() {
+        return classifier;
+    }
+
+    public String getPackaging() {
+        return packaging;
+    }
+
+    public boolean isSnapshot() {
+        return getVersion().toUpperCase().contains("SNAPSHOT");
+    }
+    
+    /** @see #customFileNameAfterArtifactMarker */
+    public String getCustomFileNameAfterArtifactMarker() {
+        return customFileNameAfterArtifactMarker;
+    }
+    
+    /** @see #customFileNameAfterArtifactMarker */
+    public void setCustomFileNameAfterArtifactMarker(String customFileNameMarker) {
+        this.customFileNameAfterArtifactMarker = customFileNameMarker;
+    }
+
+    /** @ee {@link #classifierFileNameMarker} */
+    public String getClassifierFileNameMarker() {
+        return classifierFileNameMarker!=null ? classifierFileNameMarker : getClassifier();
+    }
+    
+    /** @ee {@link #classifierFileNameMarker} */
+    public void setClassifierFileNameMarker(String classifierFileNameMarker) {
+        this.classifierFileNameMarker = classifierFileNameMarker;
+    }
+    
+    /** returns a "groupId:artifactId:version:(classifier:)packaging" string
+     * which maven refers to as the co-ordinate */
+    public String getCoordinate() {
+        return Strings.join(MutableList.<String>of().append(groupId, artifactId, packaging).
+            appendIfNotNull(classifier).append(version), ":");
+    }
+
+    public String getFilename() {
+        return artifactId+"-"+
+                (Strings.isNonEmpty(getCustomFileNameAfterArtifactMarker()) ? getCustomFileNameAfterArtifactMarker()+"-" : "")+
+                version+
+                (Strings.isNonEmpty(getClassifierFileNameMarker()) ? "-"+getClassifierFileNameMarker() : "")+
+                (Strings.isNonEmpty(getExtension()) ? "."+getExtension() : "");
+    }
+    
+    /** returns an extension, defaulting to {@link #packaging} if one cannot be inferred */
+    @Nullable public String getExtension() {
+        if ("jar".equalsIgnoreCase(packaging) || "bundle".equalsIgnoreCase(packaging))
+            return "jar";
+        if ("war".equalsIgnoreCase(packaging))
+            return "war";
+        log.debug("Unrecognised packaging for autodetecting extension, defaulting to {} for: {}", packaging, this);
+        return packaging;
+    }
+
+    @Override
+    public String toString() {
+        return JavaClassNames.simpleClassName(this)+"["+getCoordinate()+"]";
+    }
+
+    @Override
+    public int hashCode() {
+        // autogenerated code
+        
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((artifactId == null) ? 0 : artifactId.hashCode());
+        result = prime * result + ((classifier == null) ? 0 : classifier.hashCode());
+        result = prime * result + ((classifierFileNameMarker == null) ? 0 : classifierFileNameMarker.hashCode());
+        result = prime * result + ((customFileNameAfterArtifactMarker == null) ? 0 : customFileNameAfterArtifactMarker.hashCode());
+        result = prime * result + ((groupId == null) ? 0 : groupId.hashCode());
+        result = prime * result + ((packaging == null) ? 0 : packaging.hashCode());
+        result = prime * result + ((version == null) ? 0 : version.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        // autogenerated code
+        
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        MavenArtifact other = (MavenArtifact) obj;
+        if (artifactId == null) {
+            if (other.artifactId != null)
+                return false;
+        } else if (!artifactId.equals(other.artifactId))
+            return false;
+        if (classifier == null) {
+            if (other.classifier != null)
+                return false;
+        } else if (!classifier.equals(other.classifier))
+            return false;
+        if (classifierFileNameMarker == null) {
+            if (other.classifierFileNameMarker != null)
+                return false;
+        } else if (!classifierFileNameMarker.equals(other.classifierFileNameMarker))
+            return false;
+        if (customFileNameAfterArtifactMarker == null) {
+            if (other.customFileNameAfterArtifactMarker != null)
+                return false;
+        } else if (!customFileNameAfterArtifactMarker.equals(other.customFileNameAfterArtifactMarker))
+            return false;
+        if (groupId == null) {
+            if (other.groupId != null)
+                return false;
+        } else if (!groupId.equals(other.groupId))
+            return false;
+        if (packaging == null) {
+            if (other.packaging != null)
+                return false;
+        } else if (!packaging.equals(other.packaging))
+            return false;
+        if (version == null) {
+            if (other.version != null)
+                return false;
+        } else if (!version.equals(other.version))
+            return false;
+        return true;
+    }
+
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenRetriever.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenRetriever.java b/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenRetriever.java
new file mode 100644
index 0000000..d8cf89c
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/maven/MavenRetriever.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.maven;
+
+import java.io.File;
+
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+
+/** returns the URL for accessing the artifact, assuming sonatype for snapshots and maven.org for releases by default,
+ * with some methods checking local file system, and allowing the generators for each to be specified */
+public class MavenRetriever {
+
+    public static final Function<MavenArtifact,String> CONDITIONAL_SNAPSHOT_URL_GENERATOR = new Function<MavenArtifact, String>() {
+        public String apply(MavenArtifact artifact) {
+            if (artifact.groupId.startsWith("org.apache")) {
+                return APACHE_SNAPSHOT_URL_GENERATOR.apply(artifact);
+            } else {
+                return SONATYPE_SNAPSHOT_URL_GENERATOR.apply(artifact);
+            }
+        }
+    };
+
+    public static final Function<MavenArtifact,String> SONATYPE_SNAPSHOT_URL_GENERATOR = 
+            nexusSnapshotUrlGenerator("https://oss.sonatype.org");
+
+    public static final Function<MavenArtifact,String> APACHE_SNAPSHOT_URL_GENERATOR = 
+            nexusSnapshotUrlGenerator("https://repository.apache.org");
+
+    public static Function<MavenArtifact,String> nexusSnapshotUrlGenerator(final String baseUrl) {
+        return new Function<MavenArtifact, String>() {
+            public String apply(MavenArtifact artifact) {
+                return baseUrl+"/service/local/artifact/maven/redirect?" +
+                        "r=snapshots&" +
+                        "v="+Urls.encode(artifact.version)+"&" +
+                        "g="+Urls.encode(artifact.groupId)+"&" +
+                        "a="+Urls.encode(artifact.artifactId)+"&" +
+                        (artifact.classifier!=null ? "c="+Urls.encode(artifact.classifier)+"&" : "")+
+                        "e="+Urls.encode(artifact.packaging);
+            }
+        };
+    }
+
+    public static final Function<MavenArtifact,String> MAVEN_CENTRAL_URL_GENERATOR = new Function<MavenArtifact, String>() {
+        public String apply(MavenArtifact artifact) {
+            return "http://search.maven.org/remotecontent?filepath="+
+                    Urls.encode(Strings.replaceAllNonRegex(artifact.groupId, ".", "/")+"/"+
+                            artifact.artifactId+"/"+artifact.version+"/"+
+                            artifact.getFilename());
+        }
+    };
+
+    public static final Function<MavenArtifact,String> LOCAL_REPO_PATH_GENERATOR = new Function<MavenArtifact, String>() {
+        public String apply(MavenArtifact artifact) {
+            return System.getProperty("user.home")+"/.m2/repository/"+
+                    Strings.replaceAllNonRegex(artifact.groupId, ".", "/")+"/"+
+                            artifact.artifactId+"/"+artifact.version+"/"+
+                            artifact.getFilename();
+        }
+    };
+
+    protected Function<MavenArtifact,String> snapshotUrlGenerator = CONDITIONAL_SNAPSHOT_URL_GENERATOR;
+    protected Function<MavenArtifact,String> releaseUrlGenerator = MAVEN_CENTRAL_URL_GENERATOR;
+    
+    public void setSnapshotUrlGenerator(Function<MavenArtifact, String> snapshotUrlGenerator) {
+        this.snapshotUrlGenerator = snapshotUrlGenerator;
+    }
+    
+    public void setReleaseUrlGenerator(Function<MavenArtifact, String> releaseUrlGenerator) {
+        this.releaseUrlGenerator = releaseUrlGenerator;
+    }
+    
+    public String getHostedUrl(MavenArtifact artifact) {
+        if (artifact.isSnapshot()) return snapshotUrlGenerator.apply(artifact);
+        else return releaseUrlGenerator.apply(artifact);
+    }
+    
+    public String getLocalPath(MavenArtifact artifact) {
+        return LOCAL_REPO_PATH_GENERATOR.apply(artifact);
+    }
+    
+    public boolean isInstalledLocally(MavenArtifact artifact) {
+        return new File(getLocalPath(artifact)).exists();
+    }
+
+    /** returns a URL for accessing the given artifact, preferring a local file if available,
+     * else generating a hosted URL (but not checking) */
+    public String getLocalUrl(MavenArtifact artifact) {
+        if (isInstalledLocally(artifact)) return new File(getLocalPath(artifact)).toURI().toString();
+        if (artifact.isSnapshot()) return snapshotUrlGenerator.apply(artifact);
+        else return releaseUrlGenerator.apply(artifact);
+    }
+    
+    /** returns a URL for accessing the artifact from the local machine (ie preferring a local repo),
+     * using the default remote sits (sonatype for snapshots and maven.org for releases) */
+    public static String localUrl(MavenArtifact artifact) {
+        return new MavenRetriever().getLocalUrl(artifact);
+    }
+
+    /** returns a URL for accessing the artifact from any machine (ie not allowing a local repo),
+     * using the default remote sits (sonatype for snapshots and maven.org for releases) */
+    public static String hostedUrl(MavenArtifact artifact) {
+        return new MavenRetriever().getHostedUrl(artifact);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/Cidr.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/Cidr.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/Cidr.java
new file mode 100644
index 0000000..d7b564a
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/Cidr.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.math.BitList;
+import org.apache.brooklyn.util.math.BitUtils;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.collect.ImmutableList;
+
+/** represents a CIDR (classless inter-domain routing) token, i.e. 10.0.0.0/8 or 192.168.4.0/24 */
+public class Cidr implements Serializable {
+
+    private static final long serialVersionUID = -4605909101590811958L;
+
+    /** 0.0.0.0/0 -- matches all addresses */
+    public static final Cidr UNIVERSAL = new Cidr();
+    
+    public static final Cidr _10 = new Cidr(10);
+    public static final Cidr _172_16 = new Cidr("172.16.0.0/12");
+    public static final Cidr _192_168 = new Cidr(192, 168);
+    public static final Cidr CLASS_A = _10;
+    public static final Cidr CLASS_B = _172_16;
+    public static final Cidr CLASS_C = _192_168;
+    
+    public static final List<Cidr> PRIVATE_NETWORKS_RFC_1918 = ImmutableList.<Cidr>of(_192_168, _172_16, _10);
+    
+    public static final Cidr _169_254 = new Cidr("169.254.0.0/16");
+    public static final Cidr LINK_LOCAL = _169_254;
+    
+    public static final Cidr _127 = new Cidr("127.0.0.0/8");
+    public static final Cidr LOOPBACK = _127;
+
+    public static final List<Cidr> NON_PUBLIC_CIDRS = ImmutableList.<Cidr>builder().addAll(PRIVATE_NETWORKS_RFC_1918).add(LINK_LOCAL).add(LOOPBACK).build();
+
+
+    final int[] subnetBytes = new int[] { 0, 0, 0, 0 };
+    final int length;
+    
+    
+    public Cidr(String cidr) {
+        if (Strings.isBlank(cidr))
+            // useful e.g. if user leaves it blank in gui
+            cidr = "0.0.0.0/0";
+        int slash = cidr.indexOf('/');
+        if (slash==-1) throw new IllegalArgumentException("CIDR should be of form 192.168.0.0/16 (missing slash); input="+cidr);
+        String subnet = cidr.substring(0, slash);
+        String lengthS = cidr.substring(slash+1);
+        this.length = Integer.parseInt(lengthS);
+        String[] bytes = subnet.split("\\.");
+        int i=0;
+        for (; i<this.length/8; i++)
+            subnetBytes[i] = Integer.parseInt(bytes[i]);
+        for (; i<(this.length+7)/8; i++)
+            // for fractional parts: reverse significance, trim, reverse back
+            subnetBytes[i] = 
+                    BitUtils.unsigned( BitUtils.reverseBitSignificanceInByte(
+                            BitList.newInstanceFromBytes(BitUtils.reverseBitSignificanceInByte(Integer.parseInt(bytes[i]))).
+                            resized(this.length % 8).intValue() ));
+    }
+    
+    /** returns true iff this CIDR is well-formed and canonical,
+     * i.e. 4 dot-separated bytes followed by a slash and a length,
+     * where length is <= 32, and the preceding 4 bytes don't include any 1 bits beyond the indicated length; 
+     * e.g. 10.0.0.0/8 -- but not 10.0.0.1/8 or 10.../8
+     * (although the latter ones are accepted by the constructor and converted to the canonical CIDR) */
+    public static boolean isCanonical(String cidr) {
+        try {
+            return cidr.equals(new Cidr(cidr).toString());
+        } catch (Throwable e) {
+            Exceptions.propagateIfFatal(e);
+            return false;
+        }
+    }
+    
+    /** allows creation as  Cidr(192, 168)  for 192.168.0.0/16;
+     * zero bits or ints included are significant, i.e. Cidr(10, 0) gives 10.0.0.0/16 */
+    public Cidr(int ...unsignedBytes) {
+        length = unsignedBytes.length*8;
+        System.arraycopy(unsignedBytes, 0, subnetBytes, 0, unsignedBytes.length);
+    }
+
+    public Cidr(int[] subnetBytes, int length) {
+        this.length = length;
+        if (subnetBytes.length>4)
+            throw new IllegalArgumentException("Cannot create CIDR beyond 4 bytes: "+Arrays.toString(subnetBytes));
+        if (length>32)
+            throw new IllegalArgumentException("Cannot create CIDR beyond 4 bytes: length "+length);
+        // reverse the bits to remove zeroed bits, then reverse back
+        byte[] significantSubnetBytes = BitList.newInstance(BitUtils.reverseBitSignificanceInBytes(subnetBytes)).resized(length).asBytes();
+        for (int i=0; i<significantSubnetBytes.length; i++)
+            this.subnetBytes[i] = BitUtils.unsigned(BitUtils.reverseBitSignificance(significantSubnetBytes[i]));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + length;
+        result = prime * result + Arrays.hashCode(subnetBytes);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Cidr other = (Cidr) obj;
+        if (length != other.length)
+            return false;
+        if (!Arrays.equals(subnetBytes, other.subnetBytes))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        int i=0;
+        while (i<(length+7)/8) {
+            if (sb.length()>0) sb.append(".");
+            sb.append(""+subnetBytes[i]);
+            i++;
+        }
+        while (i<4) {
+            if (sb.length()>0) sb.append(".");
+            sb.append("0");
+            i++;
+        }
+        sb.append("/");
+        sb.append(""+length);
+        return sb.toString();
+    }
+
+    public int[] getBytes() {
+        return Arrays.copyOf(subnetBytes, 4);
+    }
+
+    public int getLength() {
+        return length;
+    }
+
+    public Cidr subnet(int ...extraUnsignedBytes) {
+        if ((length%8)!=0) throw new IllegalStateException("subnet can only be used for byte boundary subnetted CIDR's; not "+this);
+        int[] newBytes = getBytes();
+        int newLen = this.length + extraUnsignedBytes.length*8;
+        if (newLen>32) throw new IllegalStateException("further subnet for "+Arrays.toString(extraUnsignedBytes)+" not possible on CIDR "+this);
+        for (int i=0; i<extraUnsignedBytes.length; i++) {
+            newBytes[this.length/8 + i] = extraUnsignedBytes[i];
+        }
+        return new Cidr(newBytes, newLen);
+    }
+
+    /** returns the netmask for this cidr; e.g. for a /24 cidr returns 255.255.255.0 */
+    public InetAddress netmask() {
+        final byte[] netmaskBytes = new byte[] { 0, 0, 0, 0 };
+        int lengthLeft = length;
+        int i=0;
+        while (lengthLeft>0) {
+            if (lengthLeft>=8) {
+                netmaskBytes[i] = (byte)255;
+            } else {
+                netmaskBytes[i] = (byte) ( (1 << lengthLeft) - 1 );
+            }
+            lengthLeft -= 8;
+            i++;
+        }
+        return Networking.getInetAddressWithFixedName(netmaskBytes);
+    }
+
+    /** taking the addresses in the CIDR in order, returns the one in the offset^th position 
+     * (starting with the CIDR itself, even if final bits are 0) */
+    public InetAddress addressAtOffset(int offset) {
+        int[] ints = getBytes();
+        ints[3] += offset;
+        {
+            int i=3;
+            while (ints[i]>=256) {
+                ints[i-1] += (ints[i] / 256);
+                ints[i] %= 256;
+                i--;
+            }
+        }
+        byte[] bytes = new byte[] { 0, 0, 0, 0 };
+        for (int i=0; i<4; i++)
+            bytes[i] = (byte) ints[i];
+        return Networking.getInetAddressWithFixedName(bytes);
+    }
+
+    /** returns length of the prefix in common between the two cidrs */
+    public int commonPrefixLength(Cidr other) {
+        return asBitList().commonPrefixLength(other.asBitList());
+    }
+
+    public Cidr commonPrefix(Cidr other) {
+        return new Cidr(other.getBytes(), commonPrefixLength(other));
+    }
+
+    /** returns list of bits for the significant (length) bits of this CIDR */ 
+    public BitList asBitList() {
+        return BitList.newInstance(BitUtils.reverseBitSignificanceInBytes(getBytes())).resized(getLength());
+    }
+    
+    public boolean contains(Cidr target) {
+        return commonPrefixLength(target) == getLength();
+    }
+
+    // FIXME remove from here, promote NetworkUtils
+    /** @deprecated use {@link Networking#getInetAddressWithFixedName(byte[])} */
+    @Deprecated
+    public static InetAddress getInetAddressWithFixedName(byte[] ip) {
+        return Networking.getInetAddressWithFixedName(ip);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/HasNetworkAddresses.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/HasNetworkAddresses.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/HasNetworkAddresses.java
new file mode 100644
index 0000000..397570f
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/HasNetworkAddresses.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface HasNetworkAddresses {
+
+    /**
+     * <h4>note</h4> hostname is something that is set in the operating system.
+     * This value may or may not be set in DNS.
+     * 
+     * @return hostname of the node, or null if unknown
+     */
+    @Nullable
+    String getHostname();
+    
+    /**
+     * All public IP addresses, potentially including shared ips.
+     */
+    Set<String> getPublicAddresses();
+
+    /**
+     * All private IP addresses.
+     */
+    Set<String> getPrivateAddresses();
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/NetworkMultiAddressUtils.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/NetworkMultiAddressUtils.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/NetworkMultiAddressUtils.java
new file mode 100644
index 0000000..2babf1a
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/NetworkMultiAddressUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+import java.util.Collection;
+
+/**
+ * Given several strings, determines which have the longest, and shorted, initial matching prefix.
+ * Particularly useful as a poor-man's way to determine which IP's are likely to be the same subnet.
+ */
+public class NetworkMultiAddressUtils {
+
+    // TODO should convert to byte arrays then binary, and look at binary digit match length !
+    
+//  public static Collection<String> sortByInitialSimilarity(final String pattern, Collection<String> targets) {
+//      List<String> result = new ArrayList<String>(targets);
+//      Collections.sort(result, new Comparator<String>() {
+//          @Override
+//          public int compare(String o1, String o2) {
+//              int i1 = 0; int i2 = 0;
+//              for (int i=0; i<pattern.length(); i++) {
+//                  if (o1.length()<i || o2.length()<i) break;
+//                  if (o1.substring(0, i).equals(anObject))
+//              }
+//          }
+//      });
+//      return result;
+//  }
+  
+  public static String getClosest(final String pattern, Collection<String> targets) {
+      int score = -1;
+      String best = null;
+      for (String target: targets) {
+          int thisScore = matchLength(pattern, target);
+          if (thisScore > score) {
+              score = thisScore;
+              best = target;
+          }
+      }
+      return best;
+  }
+
+  public static String getFurthest(final String pattern, Collection<String> targets) {
+      int score = 65535;
+      String best = null;
+      for (String target: targets) {
+          int thisScore = matchLength(pattern, target);
+          if (thisScore < score) {
+              score = thisScore;
+              best = target;
+          }
+      }
+      return best;
+  }
+
+  private static int matchLength(String pattern, String target) {
+      int i=0;
+      while (i<pattern.length() && i<target.length() && pattern.charAt(i)==target.charAt(i))
+          i++;
+      return i;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java
new file mode 100644
index 0000000..695e3f8
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/Networking.java
@@ -0,0 +1,547 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.time.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Throwables;
+import com.google.common.net.HostAndPort;
+import com.google.common.primitives.UnsignedBytes;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class Networking {
+
+    private static final Logger log = LoggerFactory.getLogger(Networking.class);
+    
+    public static final int MIN_PORT_NUMBER = 1;
+    public static final int MAX_PORT_NUMBER = 65535;
+
+    // based on http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
+    // but updated to allow leading zeroes
+    public static final String VALID_IP_ADDRESS_REGEX = "^((0*[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(0*[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";
+    
+    public static final Pattern VALID_IP_ADDRESS_PATTERN;
+    static {
+        VALID_IP_ADDRESS_PATTERN = Pattern.compile(VALID_IP_ADDRESS_REGEX);
+    }
+
+    public static final List<Cidr> PRIVATE_NETWORKS = Cidr.PRIVATE_NETWORKS_RFC_1918;
+    
+    public static InetAddress ANY_NIC = getInetAddressWithFixedName(0, 0, 0, 0);
+    public static InetAddress LOOPBACK = getInetAddressWithFixedName(127, 0, 0, 1);
+        
+    public static boolean isPortAvailable(int port) {
+        return isPortAvailable(ANY_NIC, port);
+    }
+    public static boolean isPortAvailable(InetAddress localAddress, int port) {
+        if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
+            throw new IllegalArgumentException("Invalid start port: " + port);
+        }
+
+        Stopwatch watch = Stopwatch.createStarted();
+        try {
+            //despite http://stackoverflow.com/questions/434718/sockets-discover-port-availability-using-java
+            //(recommending the following) it isn't 100% reliable (e.g. nginx will happily coexist with ss+ds)
+            //
+            //Svet - SO_REUSEADDR (enabled below) will allow one socket to listen on 0.0.0.0:X and another on
+            //192.168.0.1:X which explains the above comment (nginx sets SO_REUSEADDR as well). Moreover there
+            //is no TIME_WAIT for listening sockets without any connections so why enable it at all.
+            ServerSocket ss = null;
+            DatagramSocket ds = null;
+            try {
+                // Check TCP port
+                ss = new ServerSocket();
+                ss.setSoTimeout(250);
+                ss.setReuseAddress(true);
+                ss.bind(new InetSocketAddress(localAddress, port));
+
+                // Check UDP port
+                ds = new DatagramSocket(null);
+                ds.setSoTimeout(250);
+                ds.setReuseAddress(true);
+                ds.bind(new InetSocketAddress(localAddress, port));
+            } catch (IOException e) {
+                if (log.isTraceEnabled()) log.trace("Failed binding to " + localAddress + " : " + port, e);
+                return false;
+            } finally {
+                closeQuietly(ds);
+                closeQuietly(ss);
+            }
+
+            if (localAddress==null || ANY_NIC.equals(localAddress)) {
+                // sometimes 0.0.0.0 can be bound to even if 127.0.0.1 has the port as in use;
+                // check all interfaces if 0.0.0.0 was requested
+                Enumeration<NetworkInterface> nis = null;
+                try {
+                    nis = NetworkInterface.getNetworkInterfaces();
+                } catch (SocketException e) {
+                    throw Exceptions.propagate(e);
+                }
+                // When using a specific interface saw failures not caused by port already bound:
+                //   * java.net.SocketException: No such device
+                //   * java.net.BindException: Cannot assign requested address
+                //   * probably many more
+                // Check if the address is still valid before marking the port as not available.
+                boolean foundAvailableInterface = false;
+                while (nis.hasMoreElements()) {
+                    NetworkInterface ni = nis.nextElement();
+                    Enumeration<InetAddress> as = ni.getInetAddresses();
+                    while (as.hasMoreElements()) {
+                        InetAddress a = as.nextElement();
+                        if (!isPortAvailable(a, port)) {
+                            if (isAddressValid(a)) {
+                                if (log.isTraceEnabled()) log.trace("Port {} : {} @ {} is taken and the address is valid", new Object[] {a, port, nis});
+                                return false;
+                            }
+                        } else {
+                            foundAvailableInterface = true;
+                        }
+                    }
+                }
+                if (!foundAvailableInterface) {
+                    //Aborting with an error, even nextAvailablePort won't be able to find a free port.
+                    throw new RuntimeException("Unable to bind on any network interface, even when letting the OS pick a port. Possible causes include file handle exhaustion, port exhaustion. Failed on request for " + localAddress + ":" + port + ".");
+                }
+            }
+
+            return true;
+        } finally {
+            // Until timeout was added, was taking 1min5secs for /fe80:0:0:0:1cc5:1ff:fe81:a61d%8 : 8081
+            // Svet - Probably caused by the now gone new Socket().connect() call, SO_TIMEOUT doesn't
+            // influence bind(). Doesn't hurt having it though.
+            long elapsed = watch.elapsed(TimeUnit.SECONDS);
+            boolean isDelayed = (elapsed >= 1);
+            boolean isDelayedByMuch = (elapsed >= 30);
+            if (isDelayed || log.isTraceEnabled()) {
+                String msg = "Took {} to determine if port was available for {} : {}";
+                Object[] args = new Object[] {Time.makeTimeString(watch.elapsed(TimeUnit.MILLISECONDS), true), localAddress, port};
+                if (isDelayedByMuch) {
+                    log.warn(msg, args);
+                } else if (isDelayed) {
+                    log.debug(msg, args);
+                } else {
+                    log.trace(msg, args);
+                }
+            }
+        }
+    }
+
+    /**
+     * Bind to the specified IP, but let the OS pick a port.
+     * If the operation fails we know it's not because of
+     * non-available port, the interface could be down.
+     * 
+     * If there's port exhaustion on a single interface we won't catch it
+     * and declare the port is free. Doesn't matter really because the
+     * subsequent bind of the caller will fail anyway and nextAvailablePort
+     * wouldn't be able to find a free one either.
+     */
+    private static boolean isAddressValid(InetAddress addr) {
+        ServerSocket ss;
+        try {
+            ss = new ServerSocket();
+            ss.setSoTimeout(250);
+        } catch (IOException e) {
+            throw Exceptions.propagate(e);
+        }
+        try {
+            ss.bind(new InetSocketAddress(addr, 0));
+            return true;
+        } catch (IOException e) {
+            if (log.isTraceEnabled()) log.trace("Binding on {} failed, interface could be down, being reconfigured, file handle exhaustion, port exhaustion, etc.", addr);
+            return false;
+        } finally {
+            closeQuietly(ss);
+        }
+    }
+
+    /** returns the first port available on the local machine >= the port supplied */
+    public static int nextAvailablePort(int port) {
+        checkArgument(port >= MIN_PORT_NUMBER && port <= MAX_PORT_NUMBER, "requested port %s is outside the valid range of %s to %s", port, MIN_PORT_NUMBER, MAX_PORT_NUMBER);
+        int originalPort = port;
+        while (!isPortAvailable(port) && port < MAX_PORT_NUMBER) port++;
+        if (port >= MAX_PORT_NUMBER)
+            throw new RuntimeException("unable to find a free port at or above " + originalPort);
+        return port;
+    }
+
+    public static boolean isPortValid(Integer port) {
+        return (port!=null && port>=Networking.MIN_PORT_NUMBER && port<=Networking.MAX_PORT_NUMBER);
+    }
+    public static int checkPortValid(Integer port, String errorMessage) {
+        if (!isPortValid(port)) {
+            throw new IllegalArgumentException("Invalid port value "+port+": "+errorMessage);
+        }
+        return port;
+    }
+
+    public static void checkPortsValid(Map<?, ?> ports) {
+        for (Map.Entry<?,?> entry : ports.entrySet()) {
+            Object val = entry.getValue();
+            if (val == null){
+                throw new IllegalArgumentException("port for "+entry.getKey()+" is null");
+            } else if (!(val instanceof Integer)) {
+                throw new IllegalArgumentException("port "+val+" for "+entry.getKey()+" is not an integer ("+val.getClass()+")");
+            }
+            checkPortValid((Integer)val, ""+entry.getKey());
+        }
+    }
+
+    /**
+     * Check if this is a private address, not exposed on the public Internet.
+     *
+     * For IPV4 addresses this is an RFC1918 subnet (site local) address ({@code 10.0.0.0/8},
+     * {@code 172.16.0.0/12} and {@code 192.168.0.0/16}), a link-local address
+     * ({@code 169.254.0.0/16}) or a loopback address ({@code 127.0.0.1/0}).
+     * <p>
+     * For IPV6 addresses this is the RFC3514 link local block ({@code fe80::/10})
+     * and site local block ({@code feco::/10}) or the loopback block
+     * ({@code ::1/128}).
+     *
+     * @return true if the address is private
+     */
+    public static boolean isPrivateSubnet(InetAddress address) {
+        return address.isSiteLocalAddress() || address.isLoopbackAddress() || address.isLinkLocalAddress();
+    }
+
+    /** Check whether this address is definitely not going to be usable on any other machine;
+     * i.e. if it is a loopback address or a link-local (169.254)
+     */
+    public static boolean isLocalOnly(InetAddress address) {
+        return address.isLoopbackAddress() || address.isLinkLocalAddress();
+    }
+    
+    /** As {@link #isLocalOnly(InetAddress)} but taking a string; 
+     * does not require the string to be resolvable, and generally treats non-resolvable hostnames as NOT local-only
+     * (although they are treated as private by {@link #isPrivateSubnet(String)}),
+     * although certain well-known hostnames are recognised as local-only
+     * <p>
+     * note however {@link InetAddress#getByName(String)} can ignore settings in /etc/hosts, on OS X at least, 
+     * and give different values than the system */
+    public static boolean isLocalOnly(String hostnameOrIp) {
+        Preconditions.checkNotNull(hostnameOrIp, "hostnameOrIp");
+        if ("127.0.0.1".equals(hostnameOrIp)) return true;
+        if ("localhost".equals(hostnameOrIp)) return true;
+        if ("localhost.localdomain".equals(hostnameOrIp)) return true;
+        try {
+            InetAddress ia = getInetAddressWithFixedName(hostnameOrIp);
+            return isLocalOnly(ia);
+        } catch (Exception e) {
+            log.debug("Networking cannot resolve "+hostnameOrIp+": assuming it is not a local-only address, but it is a private address");
+            return false;
+        }
+    }
+
+    /** As {@link #isPrivateSubnet(InetAddress)} but taking a string; sepcifically local-only address ARE treated as private. 
+     * does not require the string to be resolvable, and things which aren't resolvable are treated as private 
+     * unless they are known to be local-only */
+    public static boolean isPrivateSubnet(String hostnameOrIp) {
+        Preconditions.checkNotNull(hostnameOrIp, "hostnameOrIp");
+        try {
+            InetAddress ia = getInetAddressWithFixedName(hostnameOrIp);
+            return isPrivateSubnet(ia);
+        } catch (Exception e) {
+            log.debug("Networking cannot resolve "+hostnameOrIp+": assuming it IS a private address");
+            return true;
+        }
+    }
+
+    private static boolean triedUnresolvableHostname = false;
+    private static String cachedAddressOfUnresolvableHostname = null;
+    
+    /** returns null in a sane DNS environment, but if DNS provides a bogus address for made-up hostnames, this returns that address */
+    public synchronized static String getAddressOfUnresolvableHostname() {
+        if (triedUnresolvableHostname) return cachedAddressOfUnresolvableHostname;
+        String h = "noexistent-machine-"+Identifiers.makeRandomBase64Id(8);
+        try {
+            cachedAddressOfUnresolvableHostname = InetAddress.getByName(h).getHostAddress();
+            log.info("Networking detected "+cachedAddressOfUnresolvableHostname+" being returned by DNS for bogus hostnames ("+h+")");
+        } catch (Exception e) {
+            log.debug("Networking detected failure on DNS resolution of unknown hostname ("+h+" throws "+e+")");
+            cachedAddressOfUnresolvableHostname = null;
+        }
+        triedUnresolvableHostname = true;
+        return cachedAddressOfUnresolvableHostname;
+    }
+    
+    /** resolves the given hostname to an IP address, returning null if unresolvable or 
+     * if the resolution is bogus (eg 169.* subnet or a "catch-all" IP resolution supplied by some miscreant DNS services) */
+    public static InetAddress resolve(String hostname) {
+        try {
+            InetAddress a = InetAddress.getByName(hostname);
+            if (a==null) return null;
+            String ha = a.getHostAddress();
+            if (log.isDebugEnabled()) log.debug("Networking resolved "+hostname+" as "+a);
+            if (ha.equals(getAddressOfUnresolvableHostname())) return null;
+            if (ha.startsWith("169.")) return null;
+            return a;
+        } catch (Exception e) {
+            if (log.isDebugEnabled()) log.debug("Networking failed to resolve "+hostname+", threw "+e);
+            return null;
+        }
+    }
+    
+    /**
+     * Gets an InetAddress using the given IP, and using that IP as the hostname (i.e. avoids any hostname resolution).
+     * <p>
+     * This is very useful if using the InetAddress for updating config files on remote machines, because then it will
+     * not be pickup a hostname from the local /etc/hosts file, which might not be known on the remote machine.
+     */
+    public static InetAddress getInetAddressWithFixedName(byte[] ip) {
+        try {
+            StringBuilder name = new StringBuilder();
+            for (byte part : ip) {
+                if (name.length() > 0) name.append(".");
+                name.append(part);
+            }
+            return InetAddress.getByAddress(name.toString(), ip);
+        } catch (UnknownHostException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    public static InetAddress getInetAddressWithFixedName(int ip1, int ip2, int ip3, int ip4) {
+        return getInetAddressWithFixedName(asByteArray(ip1, ip2, ip3, ip4));
+    }
+
+    public static InetAddress getInetAddressWithFixedName(int ip1, int ip2, int ip3, int ip4, int ip5, int ip6) {
+        return getInetAddressWithFixedName(asByteArray(ip1, ip2, ip3, ip4, ip5, ip6));
+    }
+    
+    /** creates a byte array given a var-arg number of (or bytes or longs);
+     * checks that all values are valid as _unsigned_ bytes (i.e. in [0,255] ) */
+    public static byte[] asByteArray(long ...bytes) {
+        byte[] result = new byte[bytes.length];
+        for (int i=0; i<bytes.length; i++)
+            result[i] = UnsignedBytes.checkedCast(bytes[i]);
+        return result;
+    }
+    
+    /** checks whether given string matches a valid numeric IP (v4) address, e.g. 127.0.0.1, 
+     * but not localhost or 1.2.3.256 */
+    public static boolean isValidIp4(String input) {
+        return VALID_IP_ADDRESS_PATTERN.matcher(input).matches();
+    }
+    
+    /**
+     * Gets an InetAddress using the given hostname or IP. If it is an IPv4 address, then this is equivalent
+     * to {@link getInetAddressWithFixedName(byte[])}. If it is a hostname, then this hostname will be used
+     * in the returned InetAddress.
+     */
+    public static InetAddress getInetAddressWithFixedName(String hostnameOrIp) {
+        try {
+            if (isValidIp4(hostnameOrIp)) {
+                byte[] ip = new byte[4];
+                String[] parts = hostnameOrIp.split("\\.");
+                assert parts.length == 4 : "val="+hostnameOrIp+"; split="+Arrays.toString(parts)+"; length="+parts.length;
+                for (int i = 0; i < parts.length; i++) {
+                    ip[i] = (byte)Integer.parseInt(parts[i]);
+                }
+                return InetAddress.getByAddress(hostnameOrIp, ip);
+            } else {
+                return InetAddress.getByName(hostnameOrIp);
+            }
+        } catch (UnknownHostException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    /** returns local IP address, or 127.0.0.1 if it cannot be parsed */
+    public static InetAddress getLocalHost() {
+        try {
+            return InetAddress.getLocalHost();
+        } catch (UnknownHostException e) {
+            InetAddress result = null;
+            result = getInetAddressWithFixedName("127.0.0.1");
+            log.warn("Localhost is not resolvable; using "+result);
+            return result;
+        }
+    }
+    
+    /** returns all local addresses */
+    public static Map<String,InetAddress> getLocalAddresses() {
+        Map<String, InetAddress> result = new LinkedHashMap<String, InetAddress>();
+        Enumeration<NetworkInterface> ne;
+        try {
+            ne = NetworkInterface.getNetworkInterfaces();
+        } catch (SocketException e) {
+            log.warn("Local network interfaces are not resolvable: "+e);
+            ne = null;
+        }
+        while (ne != null && ne.hasMoreElements()) {
+            NetworkInterface nic = ne.nextElement();
+            Enumeration<InetAddress> inets = nic.getInetAddresses();
+            while (inets.hasMoreElements()) {
+                InetAddress inet = inets.nextElement();
+                result.put(inet.getHostAddress(), inet);
+            }
+        }
+        if (result.isEmpty()) {
+            log.warn("No local network addresses found; assuming 127.0.0.1");
+            InetAddress loop = Cidr.LOOPBACK.addressAtOffset(0);
+            result.put(loop.getHostAddress(), loop);
+        }
+        return result;
+    }
+
+    /** returns a CIDR object for the given string, e.g. "10.0.0.0/8" */
+    public static Cidr cidr(String cidr) {
+        return new Cidr(cidr);
+    }
+
+    /** returns any well-known private network (e.g. 10.0.0.0/8 or 192.168.0.0/16) 
+     * which the given IP is in, or the /32 of local address if none */
+    public static Cidr getPrivateNetwork(String ip) {
+        Cidr me = new Cidr(ip+"/32");
+        for (Cidr c: PRIVATE_NETWORKS)
+            if (c.contains(me)) 
+                return c;
+        return me;
+    }
+
+    public static Cidr getPrivateNetwork(InetAddress address) {
+        return getPrivateNetwork(address.getHostAddress());
+    }
+    
+    /** returns whether the IP is _not_ in any private subnet */
+    public static boolean isPublicIp(String ipAddress) {
+        Cidr me = new Cidr(ipAddress+"/32");
+        for (Cidr c: Cidr.NON_PUBLIC_CIDRS)
+            if (c.contains(me)) return false;
+        return true;
+    }
+
+    /** returns true if the supplied string matches any known IP (v4 or v6) for this machine,
+     * or if it can be resolved to any such address */
+    public static boolean isLocalhost(String remoteAddress) {
+        Map<String, InetAddress> addresses = getLocalAddresses();
+        if (addresses.containsKey(remoteAddress)) return true;
+        
+        if ("127.0.0.1".equals(remoteAddress)) return true;
+        
+        String modifiedIpV6Address = remoteAddress;
+        // IPv6 localhost "ip" strings may vary;
+        // comes back as 0:0:0:0:0:0:0:1%1 for me.
+        // following deals with the cases which seem likely.
+        // (svet suggests using InetAddress parsing but I -- Alex -- am not sure if that's going to have it's own bugs)
+        if (modifiedIpV6Address.contains("%")) {
+            // trim any description %dex
+            modifiedIpV6Address = modifiedIpV6Address.substring(0, modifiedIpV6Address.indexOf("%"));
+        }
+        if ("0:0:0:0:0:0:0:1".equals(modifiedIpV6Address)) return true;
+        if ("::1".equals(modifiedIpV6Address)) return true;
+        if (addresses.containsKey(remoteAddress) || addresses.containsKey(modifiedIpV6Address))
+            return true;
+        
+        try {
+            InetAddress remote = InetAddress.getByName(remoteAddress);
+            if (addresses.values().contains(remote))
+                return true;
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            log.debug("Error resolving address "+remoteAddress+" when checking if it is local (assuming not: "+e, e);
+        }
+        
+        return false;
+    }
+    
+    public static boolean isReachable(HostAndPort endpoint) {
+        try {
+            Socket s = new Socket(endpoint.getHostText(), endpoint.getPort());
+            closeQuietly(s);
+            return true;
+        } catch (Exception e) {
+            if (log.isTraceEnabled()) log.trace("Error reaching "+endpoint+" during reachability check (return false)", e);
+            return false;
+        }
+    }
+
+    public static void closeQuietly(Socket s) {
+        if (s != null) {
+            try {
+                s.close();
+            } catch (IOException e) {
+                /* should not be thrown */
+            }
+        }
+    }
+
+    public static void closeQuietly(ServerSocket s) {
+        if (s != null) {
+            try {
+                s.close();
+            } catch (IOException e) {
+                /* should not be thrown */
+            }
+        }
+    }
+
+    public static void closeQuietly(DatagramSocket s) {
+        if (s != null) {
+            s.close();
+        }
+    }
+
+
+    // TODO go through nic's, looking for public, private, etc, on localhost
+
+    /**
+     * force use of TLSv1, fixing:
+     * http://stackoverflow.com/questions/9828414/receiving-sslhandshakeexception-handshake-failure-despite-my-client-ignoring-al
+     */
+    public static void installTlsOnlyForHttpsForcing() {
+        System.setProperty("https.protocols", "TLSv1");
+    }
+    public static void installTlsForHttpsIfAppropriate() {
+        if (System.getProperty("https.protocols")==null && System.getProperty("brooklyn.https.protocols.leave_untouched")==null) {
+            installTlsOnlyForHttpsForcing();
+        }
+    }
+    static {
+        installTlsForHttpsIfAppropriate();
+    }
+    
+    /** does nothing, but forces the class to be loaded and do static initialization */
+    public static void init() {}
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/Protocol.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/Protocol.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/Protocol.java
new file mode 100644
index 0000000..52efd94
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/Protocol.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+public enum Protocol {
+
+    TCP("tcp"),
+    UDP("udp"),
+    ICMP("icmp"),
+    ALL("all");
+
+    final String protocol;
+
+    private Protocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    @Override
+    public String toString() {
+        return protocol;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/URLParamEncoder.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/URLParamEncoder.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/URLParamEncoder.java
new file mode 100644
index 0000000..5c761aa
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/URLParamEncoder.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+/**
+ * Encodes URLs, escaping as appropriate.
+ * 
+ * Copied from fmucar's answer in http://stackoverflow.com/questions/724043/http-url-address-encoding-in-java
+ * 
+ * TODO Want to use a library utility, but couldn't find this in guava and don't want to introduce
+ * dependency on commons-httpclient-3.1 to use URIUtil.
+ * 
+ * @author aled
+ */
+public class URLParamEncoder {
+
+    public static String encode(String input) {
+        StringBuilder resultStr = new StringBuilder();
+        for (char ch : input.toCharArray()) {
+            if (isUnsafe(ch)) {
+                resultStr.append('%');
+                resultStr.append(toHex(ch / 16));
+                resultStr.append(toHex(ch % 16));
+            } else {
+                resultStr.append(ch);
+            }
+        }
+        return resultStr.toString();
+    }
+
+    private static char toHex(int ch) {
+        return (char) (ch < 10 ? '0' + ch : 'A' + ch - 10);
+    }
+
+    private static boolean isUnsafe(char ch) {
+        if (ch > 128 || ch < 0)
+            return true;
+        return (" %$&+,/:;=?@<>#%"
+            // these are included in httpclient URI as "unwise", and have been found to be problematic
+            // * backslash in a query param breaks URI.create 
+            + "\\"
+            ).indexOf(ch) >= 0;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/Urls.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/Urls.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/Urls.java
new file mode 100644
index 0000000..0d4af35
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/Urls.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Throwables;
+import com.google.common.io.BaseEncoding;
+import com.google.common.net.MediaType;
+
+public class Urls {
+
+    public static Function<String,URI> stringToUriFunction() {
+        return StringToUri.INSTANCE;
+    }
+    
+    public static Function<String,URL> stringToUrlFunction() {
+        return StringToUrl.INSTANCE;
+    }
+    
+    private static enum StringToUri implements Function<String,URI> {
+        INSTANCE;
+        @Override public URI apply(@Nullable String input) {
+            return toUri(input);
+        }
+        @Override
+        public String toString() {
+            return "StringToUri";
+        }
+    }
+
+    private static enum StringToUrl implements Function<String,URL> {
+        INSTANCE;
+        @Override public URL apply(@Nullable String input) {
+            return toUrl(input);
+        }
+        @Override
+        public String toString() {
+            return "StringToUrl";
+        }
+    }
+
+    /** creates a URL, preserving null and propagating exceptions *unchecked* */
+    public static final URL toUrl(@Nullable String url) {
+        if (url==null) return null;
+        try {
+            return new URL(url);
+        } catch (MalformedURLException e) {
+            // FOAD
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    /** creates a URL, preserving null and propagating exceptions *unchecked* */
+    public static final URL toUrl(@Nullable URI uri) {
+        if (uri==null) return null;
+        try {
+            return uri.toURL();
+        } catch (MalformedURLException e) {
+            // FOAD
+            throw Throwables.propagate(e);
+        }
+    }
+
+    /** creates a URI, preserving null and propagating exceptions *unchecked* */
+    public static final URI toUri(@Nullable String uri) {
+        if (uri==null) return null;
+        return URI.create(uri);
+    }
+    
+    /** creates a URI, preserving null and propagating exceptions *unchecked* */
+    public static final URI toUri(@Nullable URL url) {
+        if (url==null) return null;
+        try {
+            return url.toURI();
+        } catch (URISyntaxException e) {
+            // FOAD
+            throw Throwables.propagate(e);
+        }
+    }
+
+    /** returns true if the string begins with a non-empty string of letters followed by a colon,
+     * i.e. "protocol:" returns true, but "/" returns false */
+    public static boolean isUrlWithProtocol(String x) {
+        if (x==null) return false;
+        for (int i=0; i<x.length(); i++) {
+            char c = x.charAt(i);
+            if (c==':') return i>0;
+            if (!Character.isLetter(c)) return false; 
+        }
+        return false;
+    }
+    
+    /** 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).
+     * Throws a NullPointerException if any elements of 'items' is null.
+     *  */
+    public static String mergePaths(String ...items) {
+        List<String> parts = Arrays.asList(items);
+
+        if (parts.contains(null)) {
+            throw new NullPointerException(String.format("Unable to reliably merge path from parts: %s; input contains null values", parts));
+        }
+
+        StringBuilder result = new StringBuilder();
+        for (String part: parts) {
+            boolean trimThisMerge = result.length()>0 && !result.toString().endsWith("://") && !result.toString().endsWith(":///") && !result.toString().endsWith(":");
+            if (trimThisMerge) {
+                while (result.length()>0 && result.charAt(result.length()-1)=='/')
+                    result.deleteCharAt(result.length()-1);
+                result.append('/');
+            }
+            int i = result.length();
+            result.append(part);
+            if (trimThisMerge) {
+                while (result.length()>i && result.charAt(i)=='/')
+                    result.deleteCharAt(i);
+            }
+        }
+        return result.toString();
+    }
+
+    /** encodes the string suitable for use in a URL, using default character set
+     * (non-deprecated version of URLEncoder.encode) */
+    @SuppressWarnings("deprecation")
+    public static String encode(String text) {
+        return URLEncoder.encode(text);
+    }
+    /** As {@link #encode(String)} */
+    @SuppressWarnings("deprecation")
+    public static String decode(String text) {
+        return URLDecoder.decode(text);
+    }
+
+    /** returns the protocol (e.g. http) if one appears to be specified, or else null;
+     * 'protocol' here should consist of 2 or more _letters_ only followed by a colon
+     * (2 required to prevent {@code c:\xxx} being treated as a url)
+     */
+    public static String getProtocol(String url) {
+        if (url==null) return null;
+        int i=0;
+        StringBuilder result = new StringBuilder();
+        while (true) {
+            if (url.length()<=i) return null;
+            char c = url.charAt(i);
+            if (Character.isLetter(c)) result.append(c);
+            else if (c==':') {
+                if (i>=2) return result.toString().toLowerCase();
+                return null;
+            } else return null;
+            i++;
+        }
+    }
+
+    /** return the last segment of the given url before any '?', e.g. the filename or last directory name in the case of directories
+     * (cf unix `basename`) */
+    public static String getBasename(String url) {
+        if (url==null) return null;
+        if (getProtocol(url)!=null) {
+            int firstQ = url.indexOf('?');
+            if (firstQ>=0)
+                url = url.substring(0, firstQ);
+        }
+        url = Strings.removeAllFromEnd(url, "/");
+        return url.substring(url.lastIndexOf('/')+1);
+    }
+
+    public static boolean isDirectory(String fileUrl) {
+        File file;
+        if (isUrlWithProtocol(fileUrl)) {
+            if (getProtocol(fileUrl).equals("file")) {
+                file = new File(URI.create(fileUrl));
+            } else {
+                return false;
+            }
+        } else {
+            file = new File(fileUrl);
+        }
+        return file.isDirectory();
+    }
+
+    public static File toFile(String fileUrl) {
+        if (isUrlWithProtocol(fileUrl)) {
+            if (getProtocol(fileUrl).equals("file")) {
+                return new File(URI.create(fileUrl));
+            } else {
+                throw new IllegalArgumentException("Not a file protocol URL: " + fileUrl);
+            }
+        } else {
+            return new File(fileUrl);
+        }
+    }
+
+    /** as {@link #asDataUrlBase64(String)} with plain text */
+    public static String asDataUrlBase64(String data) {
+        return asDataUrlBase64(MediaType.PLAIN_TEXT_UTF_8, data.getBytes());
+    }
+    
+    /** 
+     * Creates a "data:..." scheme URL for use with supported parsers, using Base64 encoding.
+     * (But note, by default Java's URL is not one of them, although Brooklyn's ResourceUtils does support it.)
+     * <p>
+     * It is not necessary (at least for Brookyn's routines) to base64 encode it, but recommended as that is likely more
+     * portable and easier to work with if odd characters are included.
+     * <p>
+     * It is worth noting that Base64 uses '+' which can be replaced by ' ' in some URL parsing.  
+     * But in practice it does not seem to cause issues.
+     * An alternative is to use {@link BaseEncoding#base64Url()} but it is not clear how widely that is supported
+     * (nor what parameter should be given to indicate that type of encoding, as the spec calls for 'base64'!)
+     * <p>
+     * null type means no type info will be included in the URL. */
+    public static String asDataUrlBase64(MediaType type, byte[] bytes) {
+        return "data:"+(type!=null ? type.withoutParameters().toString() : "")+";base64,"+new String(BaseEncoding.base64().encode(bytes));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/net/UserAndHostAndPort.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/net/UserAndHostAndPort.java b/utils/common/src/main/java/org/apache/brooklyn/util/net/UserAndHostAndPort.java
new file mode 100644
index 0000000..d48dccd
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/net/UserAndHostAndPort.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.net;
+
+import java.io.Serializable;
+
+import com.google.common.base.Objects;
+import com.google.common.net.HostAndPort;
+
+public class UserAndHostAndPort implements Serializable {
+
+    private static final long serialVersionUID = 1525306419968853853L;
+
+    public static UserAndHostAndPort fromParts(String user, String host, int port) {
+        return new UserAndHostAndPort(user, HostAndPort.fromParts(host, port));
+    }
+
+    public static UserAndHostAndPort fromParts(String user, HostAndPort hostAndPort) {
+        return new UserAndHostAndPort(user, hostAndPort);
+    }
+
+    /**
+     * Split a string of the form myuser@myhost:1234 into a user, host and port.
+     *  
+     * @param str The input string to parse.
+     * @return    If parsing was successful, a populated UserAndHostAndPort object.
+     * @throws IllegalArgumentException
+     *             if nothing meaningful could be parsed.
+     */
+    public static UserAndHostAndPort fromString(String str) {
+        int userEnd = str.indexOf("@");
+        if (userEnd < 0) throw new IllegalArgumentException("User missing (no '@' in "+str);
+        return new UserAndHostAndPort(str.substring(0, userEnd).trim(), HostAndPort.fromString(str.substring(userEnd+1).trim()));
+    }
+
+    private final String user;
+    private final HostAndPort hostAndPort;
+    
+    protected UserAndHostAndPort(String user, HostAndPort hostAndPort) {
+        this.user = user;
+        this.hostAndPort = hostAndPort;
+    }
+    
+    public String getUser() {
+        return user;
+    }
+    
+    public HostAndPort getHostAndPort() {
+        return hostAndPort;
+    }
+    
+    @Override
+    public String toString() {
+        return user + "@" + hostAndPort.getHostText() + (hostAndPort.hasPort() ? ":" + hostAndPort.getPort() : "");
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof UserAndHostAndPort)) return false;
+        UserAndHostAndPort o = (UserAndHostAndPort) obj;
+        return Objects.equal(user, o.user) && Objects.equal(hostAndPort, o.hostAndPort);
+    }
+    
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(user, hostAndPort);
+    }
+}


Mime
View raw message