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/18 17:03:22 UTC
[13/64] [abbrv] incubator-brooklyn git commit: BROOKLYN-162 - apply
org.apache package prefix to utils-common
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);
+ }
+}