View Javadoc
1   /*
2    * Copyright 2016 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty.resolver.dns;
18  
19  import io.netty.resolver.NameResolver;
20  import io.netty.util.concurrent.EventExecutor;
21  import io.netty.util.concurrent.Future;
22  import io.netty.util.concurrent.FutureListener;
23  import io.netty.util.concurrent.Promise;
24  import io.netty.util.internal.StringUtil;
25  
26  import java.util.List;
27  import java.util.concurrent.ConcurrentMap;
28  
29  import static io.netty.util.internal.ObjectUtil.checkNotNull;
30  
31  // FIXME(trustin): Find a better name and move it to the 'resolver' module.
32  final class InflightNameResolver<T> implements NameResolver<T> {
33  
34      private final EventExecutor executor;
35      private final NameResolver<T> delegate;
36      private final ConcurrentMap<String, Promise<T>> resolvesInProgress;
37      private final ConcurrentMap<String, Promise<List<T>>> resolveAllsInProgress;
38  
39      InflightNameResolver(EventExecutor executor, NameResolver<T> delegate,
40                           ConcurrentMap<String, Promise<T>> resolvesInProgress,
41                           ConcurrentMap<String, Promise<List<T>>> resolveAllsInProgress) {
42  
43          this.executor = checkNotNull(executor, "executor");
44          this.delegate = checkNotNull(delegate, "delegate");
45          this.resolvesInProgress = checkNotNull(resolvesInProgress, "resolvesInProgress");
46          this.resolveAllsInProgress = checkNotNull(resolveAllsInProgress, "resolveAllsInProgress");
47      }
48  
49      @Override
50      public Future<T> resolve(String inetHost) {
51          return resolve(inetHost, executor.<T>newPromise());
52      }
53  
54      @Override
55      public Future<List<T>> resolveAll(String inetHost) {
56          return resolveAll(inetHost, executor.<List<T>>newPromise());
57      }
58  
59      @Override
60      public void close() {
61          delegate.close();
62      }
63  
64      @Override
65      public Promise<T> resolve(String inetHost, Promise<T> promise) {
66          return resolve(resolvesInProgress, inetHost, promise, false);
67      }
68  
69      @Override
70      public Promise<List<T>> resolveAll(String inetHost, Promise<List<T>> promise) {
71          return resolve(resolveAllsInProgress, inetHost, promise, true);
72      }
73  
74      private <U> Promise<U> resolve(
75              final ConcurrentMap<String, Promise<U>> resolveMap,
76              final String inetHost, final Promise<U> promise, boolean resolveAll) {
77  
78          final Promise<U> earlyPromise = resolveMap.putIfAbsent(inetHost, promise);
79          if (earlyPromise != null) {
80              // Name resolution for the specified inetHost is in progress already.
81              if (earlyPromise.isDone()) {
82                  transferResult(earlyPromise, promise);
83              } else {
84                  earlyPromise.addListener(new FutureListener<U>() {
85                      @Override
86                      public void operationComplete(Future<U> f) throws Exception {
87                          transferResult(f, promise);
88                      }
89                  });
90              }
91          } else {
92              try {
93                  if (resolveAll) {
94                      @SuppressWarnings("unchecked")
95                      final Promise<List<T>> castPromise = (Promise<List<T>>) promise; // U is List<T>
96                      delegate.resolveAll(inetHost, castPromise);
97                  } else {
98                      @SuppressWarnings("unchecked")
99                      final Promise<T> castPromise = (Promise<T>) promise; // U is T
100                     delegate.resolve(inetHost, castPromise);
101                 }
102             } finally {
103                 if (promise.isDone()) {
104                     resolveMap.remove(inetHost);
105                 } else {
106                     promise.addListener(new FutureListener<U>() {
107                         @Override
108                         public void operationComplete(Future<U> f) throws Exception {
109                             resolveMap.remove(inetHost);
110                         }
111                     });
112                 }
113             }
114         }
115 
116         return promise;
117     }
118 
119     private static <T> void transferResult(Future<T> src, Promise<T> dst) {
120         if (src.isSuccess()) {
121             dst.trySuccess(src.getNow());
122         } else {
123             dst.tryFailure(src.cause());
124         }
125     }
126 
127     @Override
128     public String toString() {
129         return StringUtil.simpleClassName(this) + '(' + delegate + ')';
130     }
131 }