View Javadoc
1   /*
2    * Copyright 2014 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;
18  
19  import io.netty5.util.concurrent.EventExecutor;
20  import io.netty5.util.concurrent.FutureListener;
21  import io.netty5.util.internal.logging.InternalLogger;
22  import io.netty5.util.internal.logging.InternalLoggerFactory;
23  
24  import java.io.Closeable;
25  import java.net.SocketAddress;
26  import java.util.IdentityHashMap;
27  import java.util.Map;
28  import java.util.concurrent.ConcurrentMap;
29  
30  import static java.util.Objects.requireNonNull;
31  
32  /**
33   * Creates and manages {@link NameResolver}s so that each {@link EventExecutor} has its own resolver instance.
34   */
35  public abstract class AddressResolverGroup<T extends SocketAddress> implements Closeable {
36  
37      private static final InternalLogger logger = InternalLoggerFactory.getInstance(AddressResolverGroup.class);
38  
39      /**
40       * Note that we do not use a {@link ConcurrentMap} here because it is usually expensive to instantiate a resolver.
41       */
42      private final Map<EventExecutor, AddressResolver<T>> resolvers = new IdentityHashMap<>();
43  
44      private final Map<EventExecutor, FutureListener<Object>> executorTerminationListeners =
45              new IdentityHashMap<>();
46  
47      protected AddressResolverGroup() { }
48  
49      /**
50       * Returns the {@link AddressResolver} associated with the specified {@link EventExecutor}. If there's no associated
51       * resolver found, this method creates and returns a new resolver instance created by
52       * {@link #newResolver(EventExecutor)} so that the new resolver is reused on another
53       * {@code #getResolver(EventExecutor)} call with the same {@link EventExecutor}.
54       */
55      public AddressResolver<T> getResolver(final EventExecutor executor) {
56          requireNonNull(executor, "executor");
57  
58          if (executor.isShuttingDown()) {
59              throw new IllegalStateException("executor not accepting a task");
60          }
61  
62          AddressResolver<T> r;
63          synchronized (resolvers) {
64              r = resolvers.get(executor);
65              if (r == null) {
66                  final AddressResolver<T> newResolver;
67                  try {
68                      newResolver = newResolver(executor);
69                  } catch (Exception e) {
70                      throw new IllegalStateException("failed to create a new resolver", e);
71                  }
72  
73                  resolvers.put(executor, newResolver);
74                  FutureListener<Object> terminationListener = future -> {
75                      synchronized (resolvers) {
76                          resolvers.remove(executor);
77                          executorTerminationListeners.remove(executor);
78                      }
79                      newResolver.close();
80                  };
81                  executorTerminationListeners.put(executor, terminationListener);
82                  executor.terminationFuture().addListener(terminationListener);
83  
84                  r = newResolver;
85              }
86          }
87  
88          return r;
89      }
90  
91      /**
92       * Invoked by {@link #getResolver(EventExecutor)} to create a new {@link AddressResolver}.
93       */
94      protected abstract AddressResolver<T> newResolver(EventExecutor executor) throws Exception;
95  
96      /**
97       * Closes all {@link NameResolver}s created by this group.
98       */
99      @Override
100     @SuppressWarnings({ "unchecked", "ZeroLengthArrayAllocation" })
101     public void close() {
102         final AddressResolver<T>[] rArray;
103 
104         synchronized (resolvers) {
105             rArray = (AddressResolver<T>[]) resolvers.values().toArray(new AddressResolver[0]);
106             resolvers.clear();
107             executorTerminationListeners.clear();
108         }
109 
110         for (final AddressResolver<T> r: rArray) {
111             try {
112                 r.close();
113             } catch (Throwable t) {
114                 logger.warn("Failed to close a resolver:", t);
115             }
116         }
117     }
118 }