1 //
2 // ========================================================================
3 // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.util;
20
21 import java.net.InetSocketAddress;
22 import java.net.SocketAddress;
23 import java.nio.channels.UnresolvedAddressException;
24 import java.util.concurrent.Executor;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.TimeoutException;
27 import java.util.concurrent.atomic.AtomicBoolean;
28
29 import org.eclipse.jetty.util.log.Log;
30 import org.eclipse.jetty.util.log.Logger;
31 import org.eclipse.jetty.util.thread.Scheduler;
32
33 /**
34 * <p>Creates {@link SocketAddress} instances, returning them through a {@link Promise}.</p>
35 */
36 public interface SocketAddressResolver
37 {
38 /**
39 * Resolves the given host and port, returning a {@link SocketAddress} through the given {@link Promise}
40 * with the default timeout.
41 *
42 * @param host the host to resolve
43 * @param port the port of the resulting socket address
44 * @param promise the callback invoked when the resolution succeeds or fails
45 */
46 public void resolve(String host, int port, Promise<SocketAddress> promise);
47
48 /**
49 * <p>Creates {@link SocketAddress} instances synchronously in the caller thread.</p>
50 */
51 public static class Sync implements SocketAddressResolver
52 {
53 @Override
54 public void resolve(String host, int port, Promise<SocketAddress> promise)
55 {
56 try
57 {
58 InetSocketAddress result = new InetSocketAddress(host, port);
59 if (result.isUnresolved())
60 promise.failed(new UnresolvedAddressException());
61 else
62 promise.succeeded(result);
63 }
64 catch (Throwable x)
65 {
66 promise.failed(x);
67 }
68 }
69 }
70
71 /**
72 * <p>Creates {@link SocketAddress} instances asynchronously in a different thread.</p>
73 * <p>{@link InetSocketAddress#InetSocketAddress(String, int)} attempts to perform a DNS
74 * resolution of the host name, and this may block for several seconds.
75 * This class creates the {@link InetSocketAddress} in a separate thread and provides the result
76 * through a {@link Promise}, with the possibility to specify a timeout for the operation.</p>
77 * <p>Example usage:</p>
78 * <pre>
79 * SocketAddressResolver resolver = new SocketAddressResolver.Async(executor, scheduler, timeout);
80 * resolver.resolve("www.google.com", 80, new Promise<SocketAddress>()
81 * {
82 * public void succeeded(SocketAddress result)
83 * {
84 * // The address was resolved
85 * }
86 *
87 * public void failed(Throwable failure)
88 * {
89 * // The address resolution failed
90 * }
91 * });
92 * </pre>
93 */
94 public static class Async implements SocketAddressResolver
95 {
96 private static final Logger LOG = Log.getLogger(SocketAddressResolver.class);
97
98 private final Executor executor;
99 private final Scheduler scheduler;
100 private final long timeout;
101
102 /**
103 * Creates a new instance with the given executor (to perform DNS resolution in a separate thread),
104 * the given scheduler (to cancel the operation if it takes too long) and the given timeout, in milliseconds.
105 *
106 * @param executor the thread pool to use to perform DNS resolution in pooled threads
107 * @param scheduler the scheduler to schedule tasks to cancel DNS resolution if it takes too long
108 * @param timeout the timeout, in milliseconds, for the DNS resolution to complete
109 */
110 public Async(Executor executor, Scheduler scheduler, long timeout)
111 {
112 this.executor = executor;
113 this.scheduler = scheduler;
114 this.timeout = timeout;
115 }
116
117 public Executor getExecutor()
118 {
119 return executor;
120 }
121
122 public Scheduler getScheduler()
123 {
124 return scheduler;
125 }
126
127 public long getTimeout()
128 {
129 return timeout;
130 }
131
132 @Override
133 public void resolve(final String host, final int port, final Promise<SocketAddress> promise)
134 {
135 executor.execute(new Runnable()
136 {
137 @Override
138 public void run()
139 {
140 Scheduler.Task task = null;
141 final AtomicBoolean complete = new AtomicBoolean();
142 if (timeout > 0)
143 {
144 final Thread thread = Thread.currentThread();
145 task = scheduler.schedule(new Runnable()
146 {
147 @Override
148 public void run()
149 {
150 if (complete.compareAndSet(false, true))
151 {
152 promise.failed(new TimeoutException());
153 thread.interrupt();
154 }
155 }
156 }, timeout, TimeUnit.MILLISECONDS);
157 }
158
159 try
160 {
161 long start = System.nanoTime();
162 InetSocketAddress result = new InetSocketAddress(host, port);
163 long elapsed = System.nanoTime() - start;
164 if (LOG.isDebugEnabled())
165 LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
166 if (complete.compareAndSet(false, true))
167 {
168 if (result.isUnresolved())
169 promise.failed(new UnresolvedAddressException());
170 else
171 promise.succeeded(result);
172 }
173 }
174 catch (Throwable x)
175 {
176 if (complete.compareAndSet(false, true))
177 promise.failed(x);
178 }
179 finally
180 {
181 if (task != null)
182 task.cancel();
183 // Reset the interrupted status before releasing the thread to the pool
184 Thread.interrupted();
185 }
186 }
187 });
188 }
189 }
190 }