001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.broker;
018
019import java.net.InetAddress;
020import java.net.URI;
021import java.net.UnknownHostException;
022import java.util.HashMap;
023import java.util.Locale;
024import java.util.Map;
025
026import org.apache.activemq.util.InetAddressUtil;
027
028/**
029 * Policy object that controls how a TransportConnector publishes the connector's
030 * address to the outside world.  By default the connector will publish itself
031 * using the resolved host name of the bound server socket.
032 *
033 * @org.apache.xbean.XBean
034 */
035public class PublishedAddressPolicy {
036
037    private String clusterClientUriQuery;
038    private PublishedHostStrategy publishedHostStrategy = PublishedHostStrategy.DEFAULT;
039    private Map<Integer, Integer> portMapping = new HashMap<Integer, Integer>();
040    private Map<String, String> hostMapping = new HashMap<String, String>();
041
042    /**
043     * Defines the value of the published host value.
044     */
045    public enum PublishedHostStrategy {
046        DEFAULT,
047        IPADDRESS,
048        HOSTNAME,
049        FQDN;
050
051        public static PublishedHostStrategy getValue(String value) {
052            return valueOf(value.toUpperCase(Locale.ENGLISH));
053        }
054    }
055
056    /**
057     * Using the supplied TransportConnector this method returns the String that will
058     * be used to update clients with this connector's connect address.
059     *
060     * @param connector
061     *      The TransportConnector whose address is to be published.
062     * @return a string URI address that a client can use to connect to this Transport.
063     * @throws Exception
064     */
065    public URI getPublishableConnectURI(TransportConnector connector) throws Exception {
066
067        URI connectorURI = connector.getConnectUri();
068
069        if (connectorURI == null) {
070            return null;
071        }
072
073        String scheme = connectorURI.getScheme();
074        if ("vm".equals(scheme)) {
075            return connectorURI;
076        }
077
078        String userInfo = getPublishedUserInfoValue(connectorURI.getUserInfo());
079        String host = getPublishedHostValue(connectorURI.getHost());
080        if (hostMapping.containsKey(host)) {
081            host = hostMapping.get(host);
082        }
083
084        int port = connectorURI.getPort();
085        if (portMapping.containsKey(port)) {
086            port = portMapping.get(port);
087        }
088        String path = getPublishedPathValue(connectorURI.getPath());
089        String fragment = getPublishedFragmentValue(connectorURI.getFragment());
090
091        URI publishedURI = new URI(scheme, userInfo, host, port, path, getClusterClientUriQuery(), fragment);
092        return publishedURI;
093    }
094
095    public String getPublishableConnectString(TransportConnector connector) throws Exception {
096        return getPublishableConnectURI(connector).toString();
097    }
098
099    /**
100     * Subclasses can override what host value is published by implementing alternate
101     * logic for this method.
102     *
103     * @param uriHostEntry
104     *
105     * @return the value published for the given host.
106     *
107     * @throws UnknownHostException
108     */
109    protected String getPublishedHostValue(String uriHostEntry) throws UnknownHostException {
110
111        // By default we just republish what was already present.
112        String result = uriHostEntry;
113
114        if (this.publishedHostStrategy.equals(PublishedHostStrategy.IPADDRESS)) {
115            InetAddress address = InetAddress.getByName(uriHostEntry);
116            result = address.getHostAddress();
117        } else if (this.publishedHostStrategy.equals(PublishedHostStrategy.HOSTNAME)) {
118            InetAddress address = InetAddress.getByName(uriHostEntry);
119            if (address.isAnyLocalAddress()) {
120                // make it more human readable and useful, an alternative to 0.0.0.0
121                result = InetAddressUtil.getLocalHostName();
122            } else {
123                result = address.getHostName();
124            }
125        } else if (this.publishedHostStrategy.equals(PublishedHostStrategy.FQDN)) {
126            InetAddress address = InetAddress.getByName(uriHostEntry);
127            if (address.isAnyLocalAddress()) {
128                // make it more human readable and useful, an alternative to 0.0.0.0
129                result = InetAddressUtil.getLocalHostName();
130            } else {
131                result = address.getCanonicalHostName();
132            }
133        }
134
135        return result;
136    }
137
138    /**
139     * Subclasses can override what path value is published by implementing alternate
140     * logic for this method.  By default this method simply returns what was already
141     * set as the Path value in the original URI.
142     *
143     * @param uriPathEntry
144     *      The original value of the URI path.
145     *
146     * @return the desired value for the published URI's path.
147     */
148    protected String getPublishedPathValue(String uriPathEntry) {
149        return uriPathEntry;
150    }
151
152    /**
153     * Subclasses can override what host value is published by implementing alternate
154     * logic for this method.  By default this method simply returns what was already
155     * set as the Fragment value in the original URI.
156     *
157     * @param uriFragmentEntry
158     *      The original value of the URI Fragment.
159     *
160     * @return the desired value for the published URI's Fragment.
161     */
162    protected String getPublishedFragmentValue(String uriFragmentEntry) {
163        return uriFragmentEntry;
164    }
165
166    /**
167     * Subclasses can override what user info value is published by implementing alternate
168     * logic for this method.  By default this method simply returns what was already
169     * set as the UserInfo value in the original URI.
170     *
171     * @param uriUserInfoEntry
172     *      The original value of the URI user info.
173     *
174     * @return the desired value for the published URI's user info.
175     */
176    protected String getPublishedUserInfoValue(String uriUserInfoEntry) {
177        return uriUserInfoEntry;
178    }
179
180    /**
181     * Gets the URI query that's configured on the published URI that's sent to client's
182     * when the cluster info is updated.
183     *
184     * @return the clusterClientUriQuery
185     */
186    public String getClusterClientUriQuery() {
187        return clusterClientUriQuery;
188    }
189
190    /**
191     * Sets the URI query that's configured on the published URI that's sent to client's
192     * when the cluster info is updated.
193     *
194     * @param clusterClientUriQuery the clusterClientUriQuery to set
195     */
196    public void setClusterClientUriQuery(String clusterClientUriQuery) {
197        this.clusterClientUriQuery = clusterClientUriQuery;
198    }
199
200    /**
201     * @return the publishedHostStrategy
202     */
203    public PublishedHostStrategy getPublishedHostStrategy() {
204        return publishedHostStrategy;
205    }
206
207    /**
208     * @param strategy the publishedHostStrategy to set
209     */
210    public void setPublishedHostStrategy(PublishedHostStrategy strategy) {
211        this.publishedHostStrategy = strategy;
212    }
213
214    /**
215     * @param strategy the publishedHostStrategy to set
216     */
217    public void setPublishedHostStrategy(String strategy) {
218        this.publishedHostStrategy = PublishedHostStrategy.getValue(strategy);
219    }
220
221    /**
222     * @param portMapping map the ports in restrictive environments
223     */
224    public void setPortMapping(Map<Integer, Integer> portMapping) {
225        this.portMapping = portMapping;
226    }
227
228    public Map<Integer, Integer>  getPortMapping() {
229        return this.portMapping;
230    }
231
232    /**
233     * @param hostMapping map the resolved hosts
234     */
235    public void setHostMapping(Map<String, String> hostMapping) {
236        this.hostMapping = hostMapping;
237    }
238
239    public Map<String, String> getHostMapping() {
240        return hostMapping;
241    }
242}