2
* JBoss, Home of Professional Open Source
3
* Copyright 2005, JBoss Inc., and individual contributors as indicated
4
* by the @authors tag. See the copyright.txt in the distribution for a
5
* full listing of individual contributors.
7
* This is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU Lesser General Public License as
9
* published by the Free Software Foundation; either version 2.1 of
10
* the License, or (at your option) any later version.
12
* This software is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this software; if not, write to the Free
19
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23
package org.jboss.remoting;
25
import java.io.Serializable;
26
import java.net.InetAddress;
27
import java.net.MalformedURLException;
29
import java.net.URISyntaxException;
30
import java.net.UnknownHostException;
31
import java.security.AccessController;
32
import java.security.PrivilegedActionException;
33
import java.security.PrivilegedExceptionAction;
34
import java.util.ArrayList;
35
import java.util.Collection;
36
import java.util.Iterator;
37
import java.util.List;
39
import java.util.StringTokenizer;
40
import java.util.TreeMap;
42
import org.jboss.logging.Logger;
43
import org.jboss.remoting.serialization.SerializationStreamFactory;
44
import org.jboss.remoting.transport.ClientInvoker;
45
import org.jboss.remoting.util.SecurityUtility;
48
* InvokerLocator is an object that indentifies a specific Invoker on the network, via a unique
49
* locator URI. The locator URI is in the format: <P>
51
* <tt>protocol://host[:port][/path[?param=value¶m2=value2]]</tt> <P>
53
* For example, a http based locator might be: <P>
55
* <tt>http://192.168.10.1:8081</tt> <P>
57
* An example Socket based locator might be: <P>
59
* <tt>socket://192.168.10.1:9999</tt> <P>
61
* An example RMI based locator might be: <P>
63
* <tt>rmi://localhost</tt> <P>
65
* NOTE: If no hostname is given (e.g., "socket://:5555"), then the hostname will
66
* automatically be resolved to the outside IP address of the local machine.
67
* If the given hostname is 0.0.0.0 and the system
68
* property "jboss.bind.address" is set, then the hostname will be replaced by the value
69
* associated with 'jboss.bind.address".
71
* @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
72
* @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
73
* @version $Revision: 5918 $
75
public class InvokerLocator implements Serializable
77
static final long serialVersionUID;
78
protected static Logger log = Logger.getLogger(InvokerLocator.class);
79
protected static Boolean legacyParsingFlag;
81
protected String protocol;
82
protected String host;
83
protected ArrayList connectHomes = new ArrayList();
84
protected ArrayList homes = new ArrayList();
86
protected String path;
87
protected String query;
88
protected Map parameters;
90
private String originalURL;
91
private Home homeInUse;
96
if(Version.getDefaultVersion() == Version.VERSION_1)
98
serialVersionUID = -2909329895029296248L;
102
serialVersionUID = -4977622166779282521L;
106
public static boolean getUseLegacyParsing()
108
if (legacyParsingFlag == null)
110
return legacyParsingFlag.booleanValue();
113
public static void setUseLegacyParsing(boolean flag)
115
legacyParsingFlag = new Boolean(flag);
119
* Indicates should address binding to all network interfaces (i.e. 0.0.0.0)
121
public static final String ANY = "0.0.0.0";
123
* Constant value for server bind address system property. Value is 'jboss.bind.address'.
125
private static final String SERVER_BIND_ADDRESS = "jboss.bind.address";
128
* Public key to use when want to specify that locator look up local address by
131
public static final String BIND_BY_HOST = "remoting.bind_by_host";
135
* Constant to define the param name to be used when defining the data type.
137
public static final String DATATYPE = "datatype";
138
public static final String DATATYPE_CASED = "dataType";
141
* Constant to define the param name to be used when defining the serialization type.
143
public static final String SERIALIZATIONTYPE = "serializationtype";
144
public static final String SERIALIZATIONTYPE_CASED = "serializationType";
147
* Constant to define the param name to be used when defining the marshaller fully qualified classname
149
public static final String MARSHALLER = "marshaller";
151
* Constant to define the param name to be used when defining the unmarshaller fully qualified classname
153
public static final String UNMARSHALLER = "unmarshaller";
156
* Constant to define what port the marshalling loader port resides on.
158
public static final String LOADER_PORT = "loaderport";
161
* Constant to define the param name to be used when defining if marshalling should be by value,
162
* which means will be using local client invoker with cloning of payload value.
164
public static final String BYVALUE = "byvalue";
167
* Constant to define the param name to be used when defining if marshalling should use
168
* remote client invoker instead of using local client invoker (even though both client and
169
* server invokers are within same JVM).
171
public static final String FORCE_REMOTE = "force_remote";
174
* Constant to define if client should try to automatically establish a
175
* lease with the server. Value for this parameter key should be either 'true' or 'false'.
177
public static final String CLIENT_LEASE = "leasing";
180
* Constant to define what the client lease period should be in the case that
181
* server side leasing is turned on. Value for this parameter key should be the number
182
* of milliseconds to wait before each client lease renewal and must be greater than zero
183
* in order to be recognized.
185
public static final String CLIENT_LEASE_PERIOD = "lease_period";
189
* Constant to define if InvokerLocator should use the old (ad hoc) parsing
190
* algorithm instead of the new, URI based, parsing algorithm.
192
public static final String LEGACY_PARSING = "legacyParsing";
195
* Serves as placeholder in host position when multiple hosts are given in the
196
* query part by way of the parameter "hosts". E.g.
198
* socket://multihome:8888/?hosts=host1.jboss.org:host2.jboss.org
200
public static final String MULTIHOME = "multihome";
203
* Parameter key used for specifying multiple homes to connect to. E.g.
205
* socket://multihome/?connecthomes=host1.jboss.org:7777!host2.jboss.org:8888
207
public static final String CONNECT_HOMES_KEY = "connecthomes";
210
* Parameter key used for specifying multiple binding homes. E.g.
212
* socket://multihome/?homes=a.org:66!b.org:77&homes=c.org:88!d.org:99
214
public static final String HOMES_KEY = "homes";
217
* Parameter key used for specifying default connecting port for
218
* multihome InvokerLocator.
220
public static final String DEFAULT_CONNECT_PORT = "defaultConnectPort";
223
* Parameter key used for specifying default server bind port for
224
* multihme InvokerLocator.
226
public static final String DEFAULT_PORT = "defaultPort";
229
* Constant to determine if warning about null host should be logged.
231
public static final String SUPPRESS_HOST_WARNING = "suppressHostWarning";
234
* InvokerLocator leaves address 0.0.0.0 unchanged. Once serverBindAddress has been
235
* extracted from the InvokerLocator, it is necessary to transform 0.0.0.0 into an
236
* address that contacted over the network. See JBREM-687.
238
public static InvokerLocator validateLocator(InvokerLocator locator) throws MalformedURLException
240
InvokerLocator externalLocator = locator;
242
String host = locator.getHost();
243
String newHost = null;
244
if(host == null || InvokerLocator.ANY.equals(host))
246
// now need to get some external bindable address
249
newHost = (String)AccessController.doPrivileged( new PrivilegedExceptionAction()
251
public Object run() throws Exception
253
String bindByHost = System.getProperty(InvokerLocator.BIND_BY_HOST, "True");
254
boolean byHost = Boolean.valueOf(bindByHost).booleanValue();
257
return getLocalHost().getHostName();
261
return getLocalHost().getHostAddress();
266
catch (PrivilegedActionException e)
268
log.debug("Could not get host by name or address.", e.getCause());
273
// now what? step through network interfaces?
274
throw new RuntimeException("Can not determine bindable address for locator (" + locator + ")");
277
externalLocator = new InvokerLocator(locator.protocol, newHost, locator.port,
278
locator.getPath(), locator.getParameters());
281
return externalLocator;
285
public static void extractHomes(String homeList, List list, int defaultPort)
287
StringTokenizer tok = new StringTokenizer(homeList, "!");
288
while(tok.hasMoreTokens())
292
String token = tok.nextToken();
293
boolean isIPv6 = token.indexOf('[') >= 0;
298
int pos = token.indexOf(']');
299
if (pos + 1 == token.length())
302
boundary = token.indexOf(']') + 1;
307
boundary = token.lastIndexOf(':');
312
h = token.substring(0, boundary);
316
p = Integer.valueOf(token.substring(boundary + 1)).intValue();
318
catch (NumberFormatException e)
320
log.warn("invalid port value: " + token.substring(boundary + 1));
331
list.add(new Home(h, p));
336
public static String convertHomesListToString(List homes)
338
if (homes == null || homes.size() == 0)
341
Iterator it = homes.iterator();
342
StringBuffer b = new StringBuffer(((Home) it.next()).toString());
345
b.append("!").append(it.next());
350
private static final InetAddress LOCAL_HOST;
356
LOCAL_HOST = (InetAddress) AccessController.doPrivileged( new PrivilegedExceptionAction()
358
public Object run() throws UnknownHostException
362
return InetAddress.getLocalHost();
364
catch (UnknownHostException e)
366
return InetAddress.getByName("127.0.0.1");
371
catch (PrivilegedActionException e)
373
log.debug(InvokerLocator.class.getName() + " unable to get local host address", e.getCause());
374
throw new ExceptionInInitializerError(e.getCause());
376
catch (SecurityException e)
378
log.debug(InvokerLocator.class.getName() + " unable to get local host address", e);
383
private static InetAddress getLocalHost() throws UnknownHostException
385
if (LOCAL_HOST != null)
392
return InetAddress.getLocalHost();
394
catch (UnknownHostException e)
396
return InetAddress.getByName("127.0.0.1");
401
* Constructs the object used to identify a remoting server via simple uri format string (e.g. socket://myhost:7000).
402
* Note: the uri passed may not always be the one returned via call to getLocatorURI() as may need to change if
403
* port not specified, host is 0.0.0.0, etc. If need original uri that is passed to this constructor, need to
404
* call getOriginalURI().
406
* @throws MalformedURLException
408
public InvokerLocator(String uri)
409
throws MalformedURLException
416
private void parse(String uriString) throws MalformedURLException
418
boolean doLegacyParsing = false;
419
if (legacyParsingFlag != null)
421
doLegacyParsing = legacyParsingFlag.booleanValue();
425
String s = getSystemProperty(LEGACY_PARSING);
426
doLegacyParsing = "true".equalsIgnoreCase(s);
431
log.warn("using deprecated legacy URL parsing routine");
432
legacyParse(uriString);
441
StringTokenizer tok = new StringTokenizer(query, "&");
442
parameters = new TreeMap();
443
while(tok.hasMoreTokens())
445
String token = tok.nextToken();
446
int eq = token.indexOf("=");
447
String name = (eq > -1) ? token.substring(0, eq) : token;
448
String value = (eq > -1) ? token.substring(eq + 1) : "";
449
parameters.put(name, value);
453
if (!MULTIHOME.equals(host) && parameters != null)
455
// Use "host:port" to connect.
456
String s = (String) parameters.remove(CONNECT_HOMES_KEY);
457
if (s != null) log.warn("host != " + MULTIHOME + ": " + CONNECT_HOMES_KEY + " will be ignored");
460
if (parameters != null)
462
String homesString = (String) parameters.remove(HOMES_KEY);
463
String connectHomesString = (String) parameters.remove(CONNECT_HOMES_KEY);
464
createHomeLists(parameters, homesString, connectHomesString);
467
// rebuild it, since the host probably got resolved and the port changed
470
if (!MULTIHOME.equals(host))
472
homeInUse = new Home(host, port);
473
connectHomes.add(homeInUse);
475
homes.add(homeInUse);
479
private void URIParse(String uriString) throws MalformedURLException
483
URI uri = new URI(encodePercent(uriString));
484
protocol = uri.getScheme();
485
checkHost(originalURL, uri.getHost());
486
host = decodePercent(resolveHost(uri.getHost()));
487
port = uri.getPort();
488
path = uri.getPath();
489
query = decodePercent(uri.getQuery());
491
catch (URISyntaxException e)
493
throw new MalformedURLException(e.getMessage());
497
private void legacyParse(String uri) throws MalformedURLException
499
log.warn("Legacy InvokerLocator parsing is deprecated");
500
int i = uri.indexOf("://");
503
throw new MalformedURLException("Invalid url " + uri);
505
String tmp = uri.substring(i + 3);
506
this.protocol = uri.substring(0, i);
507
i = tmp.indexOf("/");
508
int p = tmp.indexOf(":");
511
p = (p < i ? p : -1);
515
host = resolveHost(tmp.substring(0, p).trim());
518
port = Integer.parseInt(tmp.substring(p + 1, i));
522
port = Integer.parseInt(tmp.substring(p + 1));
529
host = resolveHost(tmp.substring(0, i).trim());
533
host = resolveHost(tmp.substring(0).trim());
538
// now look for any path
539
p = tmp.indexOf("?");
542
path = tmp.substring(i + 1, p);
543
query = tmp.substring(p + 1);
547
p = tmp.indexOf("/");
550
path = tmp.substring(p + 1);
560
private static void checkHost(String uri, String host)
562
if (host == null && !getBoolean(SUPPRESS_HOST_WARNING))
564
StringBuffer sb = new StringBuffer("Host resolves to null in ");
565
sb.append(uri).append(". Perhaps the host contains an invalid character. ");
566
sb.append("See http://www.ietf.org/rfc/rfc2396.txt.");
567
log.warn(sb.toString());
571
private static final String resolveHost(String host)
575
host = fixRemoteAddress(host);
577
else if(host.indexOf("0.0.0.0") != -1)
579
String bindAddress = getSystemProperty(SERVER_BIND_ADDRESS, "0.0.0.0");
580
if(bindAddress.equals("0.0.0.0"))
582
host = fixRemoteAddress(host);
586
host = host.replaceAll("0\\.0\\.0\\.0", getSystemProperty(SERVER_BIND_ADDRESS));
592
private static String fixRemoteAddress(String address)
598
address = (String)AccessController.doPrivileged( new PrivilegedExceptionAction()
600
public Object run() throws UnknownHostException
602
String bindByHost = System.getProperty(BIND_BY_HOST, "True");
603
boolean byHost = Boolean.valueOf(bindByHost).booleanValue();
607
return getLocalHost().getHostName();
611
return getLocalHost().getHostAddress();
616
catch (PrivilegedActionException e)
618
log.debug("error", e.getCause());
626
private void createHomeLists(Map parameters, String homesString, String connectHomesString)
628
// DEFAULT_PORT value takes precedence, followed by port value.
629
int defaultPort = port;
630
String s = (String) parameters.get(DEFAULT_PORT);
631
if (s != null && s != "")
635
defaultPort = Integer.parseInt(s);
639
log.warn("invalid value for " + DEFAULT_PORT + ": " + s);
643
if (homesString != null)
645
extractHomes(homesString, homes, defaultPort);
648
// DEFAULT_CONNECT_PORT value takes precedence, followed by
649
// DEFAULT_PORT value and then port value.
650
s = (String) parameters.get(DEFAULT_CONNECT_PORT);
651
if (s != null && s != "")
655
defaultPort = Integer.parseInt(s);
659
log.warn("invalid value for " + DEFAULT_CONNECT_PORT + ": " + s);
663
if (connectHomesString != null)
665
extractHomes(connectHomesString, connectHomes, defaultPort);
671
* Constructs the object used to identify a remoting server.
678
public InvokerLocator(String protocol, String host, int port, String path, Map parameters)
680
this.protocol = protocol;
681
this.host = resolveHost(host);
683
this.path = path == null ? "" : path;
684
this.parameters = parameters == null ? new TreeMap() : new TreeMap(parameters);
686
if (this.parameters != null)
688
String homesString = (String) this.parameters.remove(HOMES_KEY);
689
String connectHomesString = (String) this.parameters.remove(CONNECT_HOMES_KEY);
690
createHomeLists(this.parameters, homesString, connectHomesString);
693
// Test for IPv6 host address.
694
if (this.host != null && this.host.indexOf(":") >= 0 && this.host.indexOf("[") == -1)
696
this.host = "[" + this.host + "]";
702
if (!MULTIHOME.equals(host))
704
homeInUse = new Home(host, port);
705
homes.add(homeInUse);
706
connectHomes.add(homeInUse);
708
if (parameters != null)
710
// Use "host:port" to connect.
711
String s = (String) parameters.remove(CONNECT_HOMES_KEY);
712
if (s != null) log.warn("host != " + MULTIHOME + ": " + CONNECT_HOMES_KEY + " will be ignored");
717
public int hashCode()
719
return uri.hashCode();
723
* Compares to see if Object passed is of type InvokerLocator and
724
* it's internal locator uri hashcode is same as this one. Note, this
725
* means is testing to see if not only the host, protocol, and port are the
726
* same, but the path and parameters as well. Therefore 'socket://localhost:9000'
727
* and 'socket://localhost:9000/foobar' would NOT be considered equal.
731
public boolean equals(Object obj)
733
return obj != null && obj instanceof InvokerLocator && uri.equals(((InvokerLocator)obj).getLocatorURI());
737
* Compares to see if InvokerLocator passed represents the same physical remoting server
738
* endpoint as this one. Unlike the equals() method, this just tests to see if protocol, host,
739
* and port are the same and ignores the path and parameters.
743
public boolean isSameEndpoint(InvokerLocator compareMe)
745
return compareMe != null && getProtocol().equalsIgnoreCase(compareMe.getProtocol()) &&
746
getHost().equalsIgnoreCase(compareMe.getHost()) && getPort() == compareMe.getPort();
749
public boolean isCompatibleWith(InvokerLocator other)
754
// If this or other comes from pre-2.4.0 Remoting.
755
if (homes == null || other.homes == null)
758
boolean result1 = getProtocol().equalsIgnoreCase(other.getProtocol()) &&
759
path.equals(other.getPath()) &&
760
parameters.equals(other.getParameters());
762
ArrayList tempHomes = connectHomes.isEmpty() ? homes : connectHomes;
763
boolean result2 = intersects(tempHomes, other.getConnectHomeList()) ||
764
intersects(tempHomes, other.getHomeList());
766
return result1 && result2;
770
* return the locator URI, in the format: <P>
772
* <tt>protocol://host[:port][/path[?param=value¶m2=value2]]</tt>
773
* Note, this may not be the same as the original uri passed as parameter to the constructor.
776
public String getLocatorURI()
781
public String getProtocol()
786
public String getHost()
788
if (host.equals(MULTIHOME) && homeInUse != null)
789
return homeInUse.host;
794
public String getActualHost()
799
public boolean isMultihome()
801
return MULTIHOME.equals(host);
804
public String getConnectHomes()
806
return convertHomesListToString(connectHomes);
809
public List getConnectHomeList()
811
if (connectHomes == null)
813
ArrayList list = new ArrayList();
814
list.add(new Home(host, port));
818
return new ArrayList(connectHomes);
821
public void setConnectHomeList(List connectHomes)
823
if (connectHomes == null)
824
this.connectHomes = new ArrayList();
826
this.connectHomes = new ArrayList(connectHomes);
831
public Home getHomeInUse()
833
if (homeInUse != null || isMultihome())
836
return new Home(host, port);
839
public void setHomeInUse(Home homeInUse)
841
this.homeInUse = homeInUse;
844
public String getHomes()
846
return convertHomesListToString(homes);
849
public List getHomeList()
853
ArrayList list = new ArrayList();
854
list.add(new Home(host, port));
855
return new ArrayList();
858
return new ArrayList(homes);
861
public void setHomeList(List homes)
864
this.homes = new ArrayList();
866
this.homes = new ArrayList(homes);
873
if (host.equals(MULTIHOME) && homeInUse != null)
874
return homeInUse.port;
879
public int getActualPort()
884
public String getPath()
889
public Map getParameters()
891
if (parameters == null)
893
parameters = new TreeMap();
898
public String toString()
900
return "InvokerLocator [" + uri + "]";
904
* Gets the original uri passed to constructor (if there was one).
907
public String getOriginalURI()
913
* narrow this invoker to a specific RemoteClientInvoker instance
918
public ClientInvoker narrow() throws Exception
922
return (ClientInvoker) AccessController.doPrivileged( new PrivilegedExceptionAction()
924
public Object run() throws Exception
926
return InvokerRegistry.createClientInvoker(InvokerLocator.this);
930
catch (PrivilegedActionException pae)
932
throw pae.getException();
937
public String findSerializationType()
939
String serializationTypeLocal = SerializationStreamFactory.JAVA;
940
if(parameters != null)
942
serializationTypeLocal = (String) parameters.get(SERIALIZATIONTYPE);
943
if(serializationTypeLocal == null)
945
serializationTypeLocal = (String) parameters.get(InvokerLocator.SERIALIZATIONTYPE_CASED);
946
if(serializationTypeLocal == null)
948
serializationTypeLocal = SerializationStreamFactory.JAVA;
953
return serializationTypeLocal;
956
protected boolean intersects(Collection c1, Collection c2)
958
Iterator it = c1.iterator();
961
if (c2.contains(it.next()))
967
protected void rebuildLocatorURI()
969
String portPart = (port > -1) ? (":" + port) : "";
970
String divider = path.startsWith("/") ? "" : "/";
971
String parametersPart = (parameters != null) ? "?" : "";
972
uri = protocol + "://" + host + portPart + divider + path + parametersPart;
974
if(parameters != null)
976
if (!homes.isEmpty())
977
parameters.put(HOMES_KEY, convertHomesListToString(homes));
978
if (!connectHomes.isEmpty())
979
parameters.put(CONNECT_HOMES_KEY, convertHomesListToString(connectHomes));
981
Iterator iter = parameters.keySet().iterator();
982
while(iter.hasNext())
984
String key = (String) iter.next();
985
String val = (String) parameters.get(key);
992
uri += key + "=" + val;
1000
parameters.remove(HOMES_KEY);
1001
parameters.remove(CONNECT_HOMES_KEY);
1005
protected static String encodePercent(String s)
1007
if (s == null) return null;
1008
StringTokenizer st = new StringTokenizer(s, "%");
1009
StringBuffer sb = new StringBuffer();
1010
int limit = st.countTokens() - 1;
1011
for (int i = 0; i < limit; i++)
1013
String token = st.nextToken();
1014
sb.append(token).append("%25");
1016
sb.append(st.nextToken());
1017
return sb.toString();
1020
protected static String decodePercent(String s)
1022
if (s == null) return null;
1023
StringBuffer sb = new StringBuffer();
1025
int index = s.indexOf("%25", fromIndex);
1028
sb.append(s.substring(fromIndex, index)).append('%');
1029
fromIndex = index + 3;
1030
index = s.indexOf("%25", fromIndex);
1032
sb.append(s.substring(fromIndex));
1033
return sb.toString();
1036
static private String getSystemProperty(final String name, final String defaultValue)
1038
if (SecurityUtility.skipAccessControl())
1039
return System.getProperty(name, defaultValue);
1041
String value = null;
1044
value = (String)AccessController.doPrivileged( new PrivilegedExceptionAction()
1046
public Object run() throws Exception
1048
return System.getProperty(name, defaultValue);
1052
catch (PrivilegedActionException e)
1054
throw (RuntimeException) e.getCause();
1060
static private String getSystemProperty(final String name)
1062
if (SecurityUtility.skipAccessControl())
1063
return System.getProperty(name);
1065
String value = null;
1068
value = (String)AccessController.doPrivileged( new PrivilegedExceptionAction()
1070
public Object run() throws Exception
1072
return System.getProperty(name);
1076
catch (PrivilegedActionException e)
1078
throw (RuntimeException) e.getCause();
1084
static private boolean getBoolean(final String name)
1086
if (SecurityUtility.skipAccessControl())
1087
return Boolean.getBoolean(name);
1089
Boolean value = null;
1092
value = (Boolean)AccessController.doPrivileged( new PrivilegedExceptionAction()
1094
public Object run() throws Exception
1096
return Boolean.valueOf(Boolean.getBoolean(name));
1100
catch (PrivilegedActionException e)
1102
throw (RuntimeException) e.getCause();
1105
return value.booleanValue();