View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.InetAddress;
22  import java.net.InetSocketAddress;
23  import java.net.SocketAddress;
24  import java.net.UnknownHostException;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.concurrent.Executor;
28  import java.util.concurrent.TimeUnit;
29  import java.util.concurrent.TimeoutException;
30  import java.util.concurrent.atomic.AtomicBoolean;
31  
32  import org.eclipse.jetty.util.annotation.ManagedAttribute;
33  import org.eclipse.jetty.util.annotation.ManagedObject;
34  import org.eclipse.jetty.util.log.Log;
35  import org.eclipse.jetty.util.log.Logger;
36  import org.eclipse.jetty.util.thread.Scheduler;
37  
38  /**
39   * <p>Creates {@link SocketAddress} instances, returning them through a {@link Promise}.</p>
40   */
41  public interface SocketAddressResolver
42  {
43      /**
44       * Resolves the given host and port, returning a {@link SocketAddress} through the given {@link Promise}
45       * with the default timeout.
46       *  @param host the host to resolve
47       * @param port the port of the resulting socket address
48       * @param promise the callback invoked when the resolution succeeds or fails
49       */
50      public void resolve(String host, int port, Promise<List<InetSocketAddress>> promise);
51  
52      /**
53       * <p>Creates {@link SocketAddress} instances synchronously in the caller thread.</p>
54       */
55      @ManagedObject("The synchronous address resolver")
56      public static class Sync implements SocketAddressResolver
57      {
58          @Override
59          public void resolve(String host, int port, Promise<List<InetSocketAddress>> promise)
60          {
61              try
62              {
63                  InetAddress[] addresses = InetAddress.getAllByName(host);
64  
65                  List<InetSocketAddress> result = new ArrayList<>(addresses.length);
66                  for (InetAddress address : addresses)
67                      result.add(new InetSocketAddress(address, port));
68  
69                  if (result.isEmpty())
70                      promise.failed(new UnknownHostException());
71                  else
72                      promise.succeeded(result);
73              }
74              catch (Throwable x)
75              {
76                  promise.failed(x);
77              }
78          }
79      }
80  
81      /**
82       * <p>Creates {@link SocketAddress} instances asynchronously in a different thread.</p>
83       * <p>{@link InetSocketAddress#InetSocketAddress(String, int)} attempts to perform a DNS
84       * resolution of the host name, and this may block for several seconds.
85       * This class creates the {@link InetSocketAddress} in a separate thread and provides the result
86       * through a {@link Promise}, with the possibility to specify a timeout for the operation.</p>
87       * <p>Example usage:</p>
88       * <pre>
89       * SocketAddressResolver resolver = new SocketAddressResolver.Async(executor, scheduler, timeout);
90       * resolver.resolve("www.google.com", 80, new Promise&lt;SocketAddress&gt;()
91       * {
92       *     public void succeeded(SocketAddress result)
93       *     {
94       *         // The address was resolved
95       *     }
96       *
97       *     public void failed(Throwable failure)
98       *     {
99       *         // The address resolution failed
100      *     }
101      * });
102      * </pre>
103      */
104     @ManagedObject("The asynchronous address resolver")
105     public static class Async implements SocketAddressResolver
106     {
107         private static final Logger LOG = Log.getLogger(SocketAddressResolver.class);
108 
109         private final Executor executor;
110         private final Scheduler scheduler;
111         private final long timeout;
112 
113         /**
114          * Creates a new instance with the given executor (to perform DNS resolution in a separate thread),
115          * the given scheduler (to cancel the operation if it takes too long) and the given timeout, in milliseconds.
116          *
117          * @param executor  the thread pool to use to perform DNS resolution in pooled threads
118          * @param scheduler the scheduler to schedule tasks to cancel DNS resolution if it takes too long
119          * @param timeout   the timeout, in milliseconds, for the DNS resolution to complete
120          */
121         public Async(Executor executor, Scheduler scheduler, long timeout)
122         {
123             this.executor = executor;
124             this.scheduler = scheduler;
125             this.timeout = timeout;
126         }
127 
128         public Executor getExecutor()
129         {
130             return executor;
131         }
132 
133         public Scheduler getScheduler()
134         {
135             return scheduler;
136         }
137 
138         @ManagedAttribute(value = "The timeout, in milliseconds, to resolve an address", readonly = true)
139         public long getTimeout()
140         {
141             return timeout;
142         }
143 
144         @Override
145         public void resolve(final String host, final int port, final Promise<List<InetSocketAddress>> promise)
146         {
147             executor.execute(() ->
148             {
149                 Scheduler.Task task = null;
150                 final AtomicBoolean complete = new AtomicBoolean();
151                 if (timeout > 0)
152                 {
153                     final Thread thread = Thread.currentThread();
154                     task = scheduler.schedule(() ->
155                     {
156                         if (complete.compareAndSet(false, true))
157                         {
158                             promise.failed(new TimeoutException());
159                             thread.interrupt();
160                         }
161                     }, timeout, TimeUnit.MILLISECONDS);
162                 }
163 
164                 try
165                 {
166                     long start = System.nanoTime();
167                     InetAddress[] addresses = InetAddress.getAllByName(host);
168                     long elapsed = System.nanoTime() - start;
169                     if (LOG.isDebugEnabled())
170                         LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
171 
172                     List<InetSocketAddress> result = new ArrayList<>(addresses.length);
173                     for (InetAddress address : addresses)
174                         result.add(new InetSocketAddress(address, port));
175 
176                     if (complete.compareAndSet(false, true))
177                     {
178                         if (result.isEmpty())
179                             promise.failed(new UnknownHostException());
180                         else
181                             promise.succeeded(result);
182                     }
183                 }
184                 catch (Throwable x)
185                 {
186                     if (complete.compareAndSet(false, true))
187                         promise.failed(x);
188                 }
189                 finally
190                 {
191                     if (task != null)
192                         task.cancel();
193                 }
194             });
195         }
196     }
197 }