View Javadoc

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&lt;SocketAddress&gt;()
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 }