]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/util/SocketAddressResolver.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / util / SocketAddressResolver.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 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  * Creates asynchronously {@link SocketAddress} instances, returning them through a {@link Promise},
35  * in order to avoid blocking on DNS lookup.
36  * <p />
37  * {@link InetSocketAddress#InetSocketAddress(String, int)} attempts to perform a DNS resolution of
38  * the host name, and this may block for several seconds.
39  * This class creates the {@link InetSocketAddress} in a separate thread and provides the result
40  * through a {@link Promise}, with the possibility to specify a timeout for the operation.
41  * <p />
42  * Example usage:
43  * <pre>
44  * SocketAddressResolver resolver = new SocketAddressResolver(executor, scheduler);
45  * resolver.resolve("www.google.com", 80, new Promise&lt;SocketAddress&gt;()
46  * {
47  *     public void succeeded(SocketAddress result)
48  *     {
49  *         // The address was resolved
50  *     }
51  *
52  *     public void failed(Throwable failure)
53  *     {
54  *         // The address resolution failed
55  *     }
56  * });
57  * </pre>
58  */
59 public class SocketAddressResolver
60 {
61     private static final Logger LOG = Log.getLogger(SocketAddressResolver.class);
62
63     private final Executor executor;
64     private final Scheduler scheduler;
65     private final long timeout;
66
67     /**
68      * Creates a new instance with the given executor (to perform DNS resolution in a separate thread),
69      * the given scheduler (to cancel the operation if it takes too long) and the given timeout, in milliseconds.
70      *
71      * @param executor the thread pool to use to perform DNS resolution in pooled threads
72      * @param scheduler the scheduler to schedule tasks to cancel DNS resolution if it takes too long
73      * @param timeout the timeout, in milliseconds, for the DNS resolution to complete
74      */
75     public SocketAddressResolver(Executor executor, Scheduler scheduler, long timeout)
76     {
77         this.executor = executor;
78         this.scheduler = scheduler;
79         this.timeout = timeout;
80     }
81
82     public Executor getExecutor()
83     {
84         return executor;
85     }
86
87     public Scheduler getScheduler()
88     {
89         return scheduler;
90     }
91
92     public long getTimeout()
93     {
94         return timeout;
95     }
96
97     /**
98      * Resolves the given host and port, returning a {@link SocketAddress} through the given {@link Promise}
99      * with the default timeout.
100      *
101      * @param host the host to resolve
102      * @param port the port of the resulting socket address
103      * @param promise the callback invoked when the resolution succeeds or fails
104      * @see #resolve(String, int, long, Promise)
105      */
106     public void resolve(String host, int port, Promise<SocketAddress> promise)
107     {
108         resolve(host, port, timeout, promise);
109     }
110
111     /**
112      * Resolves the given host and port, returning a {@link SocketAddress} through the given {@link Promise}
113      * with the given timeout.
114      *
115      * @param host the host to resolve
116      * @param port the port of the resulting socket address
117      * @param timeout the timeout, in milliseconds, for the DNS resolution to complete
118      * @param promise the callback invoked when the resolution succeeds or fails
119      */
120     protected void resolve(final String host, final int port, final long timeout, final Promise<SocketAddress> promise)
121     {
122         executor.execute(new Runnable()
123         {
124             @Override
125             public void run()
126             {
127                 Scheduler.Task task = null;
128                 final AtomicBoolean complete = new AtomicBoolean();
129                 if (timeout > 0)
130                 {
131                     final Thread thread = Thread.currentThread();
132                     task = scheduler.schedule(new Runnable()
133                     {
134                         @Override
135                         public void run()
136                         {
137                             if (complete.compareAndSet(false, true))
138                             {
139                                 promise.failed(new TimeoutException());
140                                 thread.interrupt();
141                             }
142                         }
143                     }, timeout, TimeUnit.MILLISECONDS);
144                 }
145
146                 try
147                 {
148                     long start = System.nanoTime();
149                     InetSocketAddress result = new InetSocketAddress(host, port);
150                     long elapsed = System.nanoTime() - start;
151                     LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
152                     if (complete.compareAndSet(false, true))
153                     {
154                         if (result.isUnresolved())
155                             promise.failed(new UnresolvedAddressException());
156                         else
157                             promise.succeeded(result);
158                     }
159                 }
160                 catch (Throwable x)
161                 {
162                     if (complete.compareAndSet(false, true))
163                         promise.failed(x);
164                 }
165                 finally
166                 {
167                     if (task != null)
168                         task.cancel();
169                     // Reset the interrupted status before releasing the thread to the pool
170                     Thread.interrupted();
171                 }
172             }
173         });
174     }
175 }