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    *   https://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((FutureListener<U>) f -> transferResult(f, promise));
85              }
86          } else {
87              try {
88                  if (resolveAll) {
89                      @SuppressWarnings("unchecked")
90                      final Promise<List<T>> castPromise = (Promise<List<T>>) promise; // U is List<T>
91                      delegate.resolveAll(inetHost, castPromise);
92                  } else {
93                      @SuppressWarnings("unchecked")
94                      final Promise<T> castPromise = (Promise<T>) promise; // U is T
95                      delegate.resolve(inetHost, castPromise);
96                  }
97              } finally {
98                  if (promise.isDone()) {
99                      resolveMap.remove(inetHost);
100                 } else {
101                     promise.addListener((FutureListener<U>) f -> resolveMap.remove(inetHost));
102                 }
103             }
104         }
105 
106         return promise;
107     }
108 
109     private static <T> void transferResult(Future<T> src, Promise<T> dst) {
110         if (src.isSuccess()) {
111             dst.trySuccess(src.getNow());
112         } else {
113             dst.tryFailure(src.cause());
114         }
115     }
116 
117     @Override
118     public String toString() {
119         return StringUtil.simpleClassName(this) + '(' + delegate + ')';
120     }
121 }