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