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  *   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;
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 }
132