/* * 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.core.location; import static com.google.common.base.Preconditions.checkArgument; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.brooklyn.api.location.PortRange; import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.primitives.Ints; public class PortRanges { public static final int MAX_PORT = 65535; public static final PortRange ANY_HIGH_PORT = new LinearPortRange(1024, MAX_PORT); public static class SinglePort implements PortRange, Serializable { private static final long serialVersionUID = 7446781416534230401L; final int port; private SinglePort(int port) { this.port = port; } @Override public Iterator<Integer> iterator() { return Collections.singletonList(port).iterator(); } @Override public boolean isEmpty() { return false; } @Override public boolean asBoolean() { return true; } @Override public String toString() { return //getClass().getName()+"["+ ""+port; //+"]"; } public int hashCode() { return Objects.hashCode(port); } @Override public boolean equals(Object obj) { return (obj instanceof SinglePort) && port == ((SinglePort)obj).port; } } public static class LinearPortRange implements PortRange, Serializable { private static final long serialVersionUID = -9165280509363743508L; final int start, end, delta; private LinearPortRange(int start, int end, int delta) { this.start = start; this.end = end; this.delta = delta; checkArgument(start > 0 && start <= MAX_PORT, "start port %s out of range", start); checkArgument(end > 0 && end <= MAX_PORT, "end port %s out of range", end); checkArgument(delta > 0 ? start <= end : start >= end, "start and end out of order: %s to %s, delta %s", start, end, delta); checkArgument(delta != 0, "delta must be non-zero"); } public LinearPortRange(int start, int end) { this(start, end, (start<=end?1:-1)); } @Override public Iterator<Integer> iterator() { return new Iterator<Integer>() { int next = start; boolean hasNext = true; @Override public boolean hasNext() { return hasNext; } @Override public Integer next() { if (!hasNext) throw new NoSuchElementException("Exhausted available ports"); int result = next; next += delta; if ((delta>0 && next>end) || (delta<0 && next<end)) hasNext = false; return result; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public boolean isEmpty() { return false; } @Override public boolean asBoolean() { return true; } @Override public String toString() { return //getClass().getName()+"["+ start+"-"+end; //+"]"; } @Override public int hashCode() { return Objects.hashCode(start, end, delta); } @Override public boolean equals(Object obj) { if (!(obj instanceof LinearPortRange)) return false; LinearPortRange o = (LinearPortRange) obj; return start == o.start && end == o.end && delta == o.delta; } } public static class AggregatePortRange implements PortRange, Serializable { private static final long serialVersionUID = 7332682500816739660L; final List<PortRange> ranges; private AggregatePortRange(List<PortRange> ranges) { this.ranges = ImmutableList.copyOf(ranges); } @Override public Iterator<Integer> iterator() { return Iterables.concat(ranges).iterator(); } @Override public boolean isEmpty() { for (PortRange r: ranges) if (!r.isEmpty()) return false; return true; } @Override public boolean asBoolean() { return !isEmpty(); } @Override public String toString() { String s = ""; for (PortRange r: ranges) { if (s.length()>0) s+=","; s += r; } return //getClass().getName()+"["+ s; //+"]"; } public int hashCode() { return Objects.hashCode(ranges); } @Override public boolean equals(Object obj) { return (obj instanceof AggregatePortRange) && ranges.equals(((AggregatePortRange)obj).ranges); } } public static PortRange fromInteger(int x) { return new SinglePort(x); } public static PortRange fromIterable(Iterable<?> c) { List<PortRange> l = new ArrayList<PortRange>(); for (Object o: c) { if (o instanceof Integer) l.add(fromInteger((Integer)o)); else if (o instanceof String) for (String string : JavaStringEscapes.unwrapJsonishListIfPossible((String)o)) l.add(fromString(string)); else if (o instanceof Iterable) l.add(fromIterable((Iterable<?>)o)); else if (o instanceof int[]) l.add(fromIterable(Ints.asList((int[])o))); else if (o instanceof String[]) for (String string : (String[])o) l.add(fromString(string)); else if (o instanceof Object[]) for (Object object : (Object[])o) if (object instanceof Integer) l.add(fromInteger((Integer)object)); else if (object instanceof String) l.add(fromString((String)object)); else throw new IllegalArgumentException("'" + object + "' must be of type Integer or String"); else l.add(TypeCoercions.coerce(o, PortRange.class)); } return new AggregatePortRange(l); } /** parses a string representation of ports, as "80,8080,8000,8080-8099" */ public static PortRange fromString(String s) { List<PortRange> l = new ArrayList<PortRange>(); for (String si: s.split(",")) { si = si.trim(); int start, end; if (si.endsWith("+")) { String si2 = si.substring(0, si.length()-1).trim(); start = Integer.parseInt(si2); end = MAX_PORT; } else if (si.indexOf('-')>0) { int v = si.indexOf('-'); start = Integer.parseInt(si.substring(0, v).trim()); end = Integer.parseInt(si.substring(v+1).trim()); } else if (si.length()==0) { //nothing, ie empty range, just continue continue; } else { //should be number on its own l.add(new SinglePort(Integer.parseInt(si))); continue; } l.add(new LinearPortRange(start, end)); } if (l.size() == 1) { return l.get(0); } else { return new AggregatePortRange(l); } } private static AtomicBoolean initialized = new AtomicBoolean(false); /** performs the language extensions required for this project */ @SuppressWarnings("rawtypes") public static void init() { if (initialized.get()) return; synchronized (initialized) { if (initialized.get()) return; TypeCoercions.registerAdapter(Integer.class, PortRange.class, new Function<Integer,PortRange>() { public PortRange apply(Integer x) { return fromInteger(x); } }); TypeCoercions.registerAdapter(String.class, PortRange.class, new Function<String,PortRange>() { public PortRange apply(String x) { return fromString(x); } }); TypeCoercions.registerAdapter(Iterable.class, PortRange.class, new Function<Iterable,PortRange>() { public PortRange apply(Iterable x) { return fromIterable(x); } }); initialized.set(true); } } static { init(); } }