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: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Mikhail A. Markov
021: * @version $Revision: 1.1.2.3 $
022: */package org.apache.harmony.rmi.transport;
023:
024: import java.io.IOException;
025: import java.io.ObjectInput;
026: import java.io.ObjectOutput;
027: import java.net.InetAddress;
028: import java.net.ServerSocket;
029: import java.net.Socket;
030: import java.net.UnknownHostException;
031: import java.rmi.ConnectIOException;
032: import java.rmi.RemoteException;
033: import java.rmi.server.RMIClientSocketFactory;
034: import java.rmi.server.RMIServerSocketFactory;
035: import java.security.AccessController;
036:
037: import org.apache.harmony.rmi.common.CreateThreadAction;
038: import org.apache.harmony.rmi.common.GetBooleanPropAction;
039: import org.apache.harmony.rmi.common.GetLongPropAction;
040: import org.apache.harmony.rmi.common.GetStringPropAction;
041: import org.apache.harmony.rmi.common.RMIProperties;
042: import org.apache.harmony.rmi.internal.nls.Messages;
043:
044: /**
045: * Endpoint implementation: it contains information about host, port,
046: * client-side and server-side socket factories.
047: *
048: * @author Mikhail A. Markov
049: * @version $Revision: 1.1.2.3 $
050: */
051: public class Endpoint {
052:
053: /** Indicates null client-side factory. */
054: public static final int NULL_CSF = 0x00;
055:
056: /** Indicates non-null client-side factory. */
057: public static final int NONNULL_CSF = 0x01;
058:
059: // Host address.
060: private final String host;
061:
062: // Port number.
063: private int port;
064:
065: // Client-side socket factory.
066: private RMIClientSocketFactory csf;
067:
068: // Server-side socket factory.
069: private RMIServerSocketFactory ssf;
070:
071: // Local host address.
072: private static String localHost = getLocalHost();
073:
074: // True if local host has been obtained and is not equal to 127.0.0.1
075: private static boolean isLocalHostIdentified = false;
076:
077: // If non-null then it'll be used as a result of getLocalHost() method call.
078: private static String localHostPropVal = (String) AccessController
079: .doPrivileged(new GetStringPropAction(
080: RMIProperties.HOSTNAME_PROP));
081:
082: // If true then we will forcibly use FQDN by default.
083: private static boolean useLocalHostName = ((Boolean) AccessController
084: .doPrivileged(new GetBooleanPropAction(
085: RMIProperties.USELOCALHOSTNAME_PROP)))
086: .booleanValue();
087:
088: /*
089: * The time that we will wait to obtain FQDN for this local host (in ms).
090: * The default value is 10000 ms (10 sec.).
091: */
092: private static int localHostNameTimeout = ((Long) AccessController
093: .doPrivileged(new GetLongPropAction(
094: RMIProperties.LOCALHOSTNAMETIMEOUT_PROP, 10000)))
095: .intValue();
096:
097: /**
098: * Constructs Local Endpoint.
099: *
100: * @param port port number
101: * @param csf client-side socket factory
102: * @param ssf server-side socket factory
103: */
104: public Endpoint(int port, RMIClientSocketFactory csf,
105: RMIServerSocketFactory ssf) {
106: host = getLocalHost();
107: this .port = port;
108: this .csf = csf;
109: this .ssf = ssf;
110: }
111:
112: /**
113: * Constructs Endpoint.
114: *
115: * @param host host address/name
116: * @param port port number
117: * @param csf client-side socket factory
118: * @param ssf server-side socket factory
119: */
120: public Endpoint(String host, int port, RMIClientSocketFactory csf,
121: RMIServerSocketFactory ssf) {
122: this .host = host;
123: this .port = port;
124: this .csf = csf;
125: this .ssf = ssf;
126: }
127:
128: /**
129: * Returns Endpoint created from the given Endpoint but having null host
130: * (Such Endpoints are used for local endpoints for comparison).
131: *
132: * @param ep Endpoint to create template from
133: *
134: * @return created Endpoint
135: */
136: public static Endpoint createTemplate(Endpoint ep) {
137: return new Endpoint(null, ep.port, ep.csf, ep.ssf);
138: }
139:
140: /**
141: * Creates and returns server socket.
142: *
143: * @return created server socket
144: */
145: public ServerSocket createServerSocket() throws IOException {
146: ServerSocket ss = DefaultRMISocketFactory
147: .getNonNullServerFactory(ssf).createServerSocket(port);
148:
149: if (port == 0) {
150: port = ss.getLocalPort();
151: }
152: return ss;
153: }
154:
155: /**
156: * Creates and returns socket.
157: *
158: * @return created socket
159: */
160: public Socket createSocket() throws RemoteException {
161: Socket s;
162:
163: try {
164: s = DefaultRMISocketFactory.getNonNullClientFactory(csf)
165: .createSocket(host, port);
166: } catch (java.net.UnknownHostException uhe) {
167: // rmi.80=Unable to connect to server {0}
168: throw new java.rmi.UnknownHostException(Messages.getString(
169: "rmi.80", toString()), uhe); //$NON-NLS-1$
170: } catch (java.net.ConnectException ce) {
171: throw new java.rmi.ConnectException(Messages.getString(
172: "rmi.80", toString()), ce); //$NON-NLS-1$
173: } catch (IOException ioe) {
174: throw new ConnectIOException(Messages.getString(
175: "rmi.80", toString()), ioe); //$NON-NLS-1$
176: }
177: return s;
178: }
179:
180: /**
181: * Returns client-side socket factory of this endpoint.
182: *
183: * @return client-side socket factory of this endpoint
184: */
185: public RMIClientSocketFactory getClientSocketFactory() {
186: return csf;
187: }
188:
189: /**
190: * Returns server-side socket factory of this endpoint.
191: *
192: * @return server-side socket factory of this endpoint
193: */
194: public RMIServerSocketFactory getServerSocketFactory() {
195: return ssf;
196: }
197:
198: /**
199: * Returns the port of this endpoint.
200: *
201: * @return the port of this endpoint
202: */
203: public int getPort() {
204: return port;
205: }
206:
207: /**
208: * Returns the host address of this endpoint.
209: *
210: * @return the host address of this endpoint
211: */
212: public String getHost() {
213: return host;
214: }
215:
216: /**
217: * Compares this Endpoint with another object. Returns true if the given
218: * object is an instance of TcpEndpoint and has the same host, port, csf and
219: * ssf fields.
220: *
221: * @return true if objects are equal and false otherwise
222: */
223: public boolean equals(Object obj) {
224: if (!(obj instanceof Endpoint)) {
225: return false;
226: }
227: Endpoint anotherEp = (Endpoint) obj;
228:
229: if (port != anotherEp.port) {
230: return false;
231: }
232:
233: if ((host == null) ? (host != anotherEp.host) : !host
234: .equals(anotherEp.host)) {
235: return false;
236: }
237:
238: if ((csf == null) ? (csf != anotherEp.csf) : !csf
239: .equals(anotherEp.csf)) {
240: return false;
241: }
242:
243: if ((ssf == null) ? (ssf != anotherEp.ssf) : !ssf
244: .equals(anotherEp.ssf)) {
245: return false;
246: }
247: return true;
248: }
249:
250: /**
251: * Returns hashCode for this Endpoint.
252: *
253: * @return hashCode for this Endpoint
254: */
255: public int hashCode() {
256: return port;
257: }
258:
259: /**
260: * Writes this Endpoint to the given ObjectOutput.
261: *
262: * @param out ObjectOutput to write this Endpoint to
263: * @param writeCsf do we need to write client-side factory or not
264: *
265: * @throws IOException if any I/O error occurred during writing
266: */
267: public void writeExternal(ObjectOutput out, boolean writeCsf)
268: throws IOException {
269: if (writeCsf) {
270: if (csf == null) {
271: out.writeByte(NULL_CSF);
272: } else {
273: out.writeByte(NONNULL_CSF);
274: }
275: }
276: out.writeUTF(host);
277: out.writeInt(port);
278:
279: if (writeCsf && csf != null) {
280: out.writeObject(csf);
281: }
282: }
283:
284: /**
285: * Reads data for creating Endpoint object from the specified input stream.
286: *
287: * @param in the stream to read data from
288: * @param readCsf do we need to read client-side factory or not
289: *
290: * @return created Endpoint
291: *
292: * @throws IOException if any I/O error occurred
293: * @throws ClassNotFoundException if class could not be loaded by current
294: * class loader
295: */
296: public static Endpoint readExternal(ObjectInput in, boolean readCsf)
297: throws IOException, ClassNotFoundException {
298: int inCsf = NULL_CSF;
299:
300: if (readCsf) {
301: inCsf = in.readUnsignedByte();
302: }
303: String host = (String) in.readUTF();
304: int port = in.readInt();
305: RMIClientSocketFactory csf = null;
306:
307: if (readCsf && inCsf == NONNULL_CSF) {
308: csf = (RMIClientSocketFactory) in.readObject();
309: }
310: return new Endpoint(host, port, csf, null);
311: }
312:
313: /**
314: * Returns string representation of this Endpoint.
315: *
316: * @return string representation of this Endpoint
317: */
318: public String toString() {
319: String str = "[" + host + ":" + port; //$NON-NLS-1$ //$NON-NLS-2$
320:
321: if (csf != null) {
322: str += ", csf: " + csf; //$NON-NLS-1$
323: }
324:
325: if (ssf != null) {
326: str += ", ssf: " + ssf; //$NON-NLS-1$
327: }
328: return str + "]"; //$NON-NLS-1$
329: }
330:
331: /*
332: * Returns local host. If local host was already obtained, then return it
333: * as a result. Otherwise perform the following steps:
334: * 1) Reads java.rmi.server.hostname property if it is not equal to zero
335: * then returns it's value
336: * 2) Obtains local host by calling InetAddress.getLocalHost()
337: * 3) If java.rmi.server.useLocalHostname property is set to true then tries
338: * to obtain FQDN (fully qualified domain name) for hostname obtained
339: * in step 2 and returns it as a result.
340: * 4) If property above is not set (or set to false) then returns the result
341: * of InetAddress.getLocalHost().getHostAddress() method call.
342: *
343: * @return local host
344: */
345: private static synchronized String getLocalHost() {
346: if (isLocalHostIdentified) {
347: return localHost;
348: }
349: if (localHostPropVal != null) {
350: isLocalHostIdentified = true;
351: localHost = localHostPropVal;
352: return localHost;
353: }
354:
355: try {
356: InetAddress iaddr = InetAddress.getLocalHost();
357: byte[] addr = iaddr.getAddress();
358:
359: if (useLocalHostName) {
360: localHost = getFQDN(iaddr);
361: } else {
362: localHost = iaddr.getHostAddress();
363: }
364: isLocalHostIdentified = true;
365: } catch (Exception ex) {
366: localHost = null;
367: }
368: return localHost;
369: }
370:
371: /*
372: * Returns Fully Qualified Domain Name (FQDN) for the specified InetAddress.
373: * It'll try to obtain this name no longer then the time specified
374: * in harmony.rmi.transport.tcp.localHostNameTimeOut property.
375: *
376: * @param iaddr InetAddress to obtain FQDN
377: *
378: * @return obtained FQDN for the given InetAddress
379: */
380: private static String getFQDN(InetAddress iaddr)
381: throws UnknownHostException {
382: String hostName = iaddr.getHostName();
383:
384: if (hostName.indexOf('.') >= 0) {
385: // contains dots (so we think that it is a FQDN already)
386: return hostName;
387: }
388:
389: // does not contain dots, so we presume that getHostName()
390: // did not return fqdn
391: String addr = iaddr.getHostAddress();
392: FQDNGetter getter = new FQDNGetter(addr);
393: Thread fqdnThread = (Thread) AccessController
394: .doPrivileged(new CreateThreadAction(getter,
395: "FQDN getter.", true)); //$NON-NLS-1$
396:
397: try {
398: synchronized (getter) {
399: fqdnThread.start();
400: getter.wait(localHostNameTimeout);
401: }
402: } catch (InterruptedException ie) {
403: }
404: String fqdn = getter.getFQDN();
405:
406: if (fqdn == null || fqdn.indexOf('.') < 0) {
407: return addr;
408: }
409: return fqdn;
410: }
411:
412: /*
413: * Obtains Fully Qualified Domain Name.
414: */
415: private static class FQDNGetter implements Runnable {
416: private String addr;
417: private String name;
418:
419: FQDNGetter(String addr) {
420: this .addr = addr;
421: }
422:
423: public void run() {
424: try {
425: name = InetAddress.getByName(addr).getHostName();
426: } catch (UnknownHostException uhe) {
427: } finally {
428: synchronized (this ) {
429: notify();
430: }
431: }
432: }
433:
434: private String getFQDN() {
435: return name;
436: }
437: }
438: }
|