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.netty5.resolver.dns;
18  
19  import io.netty5.resolver.NameResolver;
20  import io.netty5.util.concurrent.EventExecutor;
21  import io.netty5.util.concurrent.Future;
22  import io.netty5.util.concurrent.Promise;
23  import io.netty5.util.internal.StringUtil;
24  
25  import java.util.List;
26  import java.util.concurrent.ConcurrentMap;
27  
28  import static java.util.Objects.requireNonNull;
29  
30  // FIXME(trustin): Find a better name and move it to the 'resolver' module.
31  final class InflightNameResolver<T> implements NameResolver<T> {
32  
33      private final EventExecutor executor;
34      private final NameResolver<T> delegate;
35      private final ConcurrentMap<String, Promise<T>> resolvesInProgress;
36      private final ConcurrentMap<String, Promise<List<T>>> resolveAllsInProgress;
37  
38      InflightNameResolver(EventExecutor executor, NameResolver<T> delegate,
39                           ConcurrentMap<String, Promise<T>> resolvesInProgress,
40                           ConcurrentMap<String, Promise<List<T>>> resolveAllsInProgress) {
41  
42          this.executor = requireNonNull(executor, "executor");
43          this.delegate = requireNonNull(delegate, "delegate");
44          this.resolvesInProgress = requireNonNull(resolvesInProgress, "resolvesInProgress");
45          this.resolveAllsInProgress = requireNonNull(resolveAllsInProgress, "resolveAllsInProgress");
46      }
47  
48      @Override
49      public Future<T> resolve(String inetHost) {
50          return resolve(inetHost, executor.newPromise());
51      }
52  
53      @Override
54      public Future<List<T>> resolveAll(String inetHost) {
55          return resolveAll(inetHost, executor.newPromise());
56      }
57  
58      @Override
59      public void close() {
60          delegate.close();
61      }
62  
63      @Override
64      public Future<T> resolve(String inetHost, Promise<T> promise) {
65          return resolve(resolvesInProgress, inetHost, promise, false);
66      }
67  
68      @Override
69      public Future<List<T>> resolveAll(String inetHost, Promise<List<T>> promise) {
70          return resolve(resolveAllsInProgress, inetHost, promise, true);
71      }
72  
73      private <U> Future<U> resolve(
74              final ConcurrentMap<String, Promise<U>> resolveMap,
75              final String inetHost, final Promise<U> promise, boolean resolveAll) {
76  
77          final Promise<U> earlyPromise = resolveMap.putIfAbsent(inetHost, promise);
78          if (earlyPromise != null) {
79              // Name resolution for the specified inetHost is in progress already.
80              Future<U> earlyFuture = earlyPromise.asFuture();
81              if (earlyFuture.isDone()) {
82                  transferResult(earlyFuture, promise);
83              } else {
84                  earlyFuture.addListener(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.asFuture().addListener(f -> resolveMap.remove(inetHost));
102                 }
103             }
104         }
105 
106         return promise.asFuture();
107     }
108 
109     private static <T> void transferResult(Future<? extends 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 }