View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.core.service;
21  
22  import java.lang.reflect.Constructor;
23  import java.util.Arrays;
24  import java.util.concurrent.Executor;
25  import java.util.concurrent.ExecutorService;
26  import java.util.concurrent.Executors;
27  import java.util.concurrent.ThreadPoolExecutor;
28  
29  import org.apache.mina.core.RuntimeIoException;
30  import org.apache.mina.core.session.AbstractIoSession;
31  import org.apache.mina.core.session.AttributeKey;
32  import org.apache.mina.core.session.IoSession;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  /**
37   * An {@link IoProcessor} pool that distributes {@link IoSession}s into one or more
38   * {@link IoProcessor}s. Most current transport implementations use this pool internally
39   * to perform better in a multi-core environment, and therefore, you won't need to 
40   * use this pool directly unless you are running multiple {@link IoService}s in the
41   * same JVM.
42   * <p>
43   * If you are running multiple {@link IoService}s, you could want to share the pool
44   * among all services.  To do so, you can create a new {@link SimpleIoProcessorPool}
45   * instance by yourself and provide the pool as a constructor parameter when you
46   * create the services.
47   * <p>
48   * This pool uses Java reflection API to create multiple {@link IoProcessor} instances.
49   * It tries to instantiate the processor in the following order:
50   * <ol>
51   * <li>A public constructor with one {@link ExecutorService} parameter.</li>
52   * <li>A public constructor with one {@link Executor} parameter.</li>
53   * <li>A public default constructor</li>
54   * </ol>
55   * The following is an example for the NIO socket transport:
56   * <pre><code>
57   * // Create a shared pool.
58   * SimpleIoProcessorPool&lt;NioSession&gt; pool = 
59   *         new SimpleIoProcessorPool&lt;NioSession&gt;(NioProcessor.class, 16);
60   * 
61   * // Create two services that share the same pool.
62   * SocketAcceptor acceptor = new NioSocketAcceptor(pool);
63   * SocketConnector connector = new NioSocketConnector(pool);
64   * 
65   * ...
66   * 
67   * // Release related resources.
68   * connector.dispose();
69   * acceptor.dispose();
70   * pool.dispose();
71   * </code></pre>
72   * 
73   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
74   * 
75   * @param <S> the type of the {@link IoSession} to be managed by the specified
76   *            {@link IoProcessor}.
77   */
78  public class SimpleIoProcessorPool<S extends AbstractIoSession> implements IoProcessor<S> {
79      /** A logger for this class */
80      private final static Logger LOGGER = LoggerFactory.getLogger(SimpleIoProcessorPool.class);
81  
82      /** The default pool size, when no size is provided. */
83      private static final int DEFAULT_SIZE = Runtime.getRuntime().availableProcessors() + 1;
84  
85      /** A key used to store the processor pool in the session's Attributes */
86      private static final AttributeKey PROCESSOR = new AttributeKey( SimpleIoProcessorPool.class, "processor");
87  
88      /** The pool table */
89      private final IoProcessor<S>[] pool;
90  
91      /** The contained  which is passed to the IoProcessor when they are created */
92      private final Executor executor;
93  
94      /** A flag set to true if we had to create an executor */
95      private final boolean createdExecutor;
96  
97      /** A lock to protect the disposal against concurrent calls */
98      private final Object disposalLock = new Object();
99  
100     /** A flg set to true if the IoProcessor in the pool are being disposed */
101     private volatile boolean disposing;
102 
103     /** A flag set to true if all the IoProcessor contained in the pool have been disposed */
104     private volatile boolean disposed;
105 
106     /**
107      * Creates a new instance of SimpleIoProcessorPool with a default
108      * size of NbCPUs +1.
109      *
110      * @param processorType The type of IoProcessor to use
111      */
112     public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType) {
113         this(processorType, null, DEFAULT_SIZE);
114     }
115 
116     /**
117      * Creates a new instance of SimpleIoProcessorPool with a defined
118      * number of IoProcessors in the pool
119      *
120      * @param processorType The type of IoProcessor to use
121      * @param size The number of IoProcessor in the pool
122      */
123     public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType, int size) {
124         this(processorType, null, size);
125     }
126 
127     /**
128      * Creates a new instance of SimpleIoProcessorPool with an executor
129      *
130      * @param processorType The type of IoProcessor to use
131      * @param executor The {@link Executor}
132      */
133     public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType, Executor executor) {
134         this(processorType, executor, DEFAULT_SIZE);
135     }
136 
137     /**
138      * Creates a new instance of SimpleIoProcessorPool with an executor
139      *
140      * @param processorType The type of IoProcessor to use
141      * @param executor The {@link Executor}
142      * @param size The number of IoProcessor in the pool
143      */
144     @SuppressWarnings("unchecked")
145     public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType,
146             Executor executor, int size) {
147         if (processorType == null) {
148             throw new IllegalArgumentException("processorType");
149         }
150 
151         if (size <= 0) {
152             throw new IllegalArgumentException("size: " + size
153                     + " (expected: positive integer)");
154         }
155 
156         // Create the executor if none is provided
157         createdExecutor = (executor == null);
158         
159         if (createdExecutor) {
160             this.executor = Executors.newCachedThreadPool();
161             // Set a default reject handler
162             ((ThreadPoolExecutor)this.executor).setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() );
163         } else {
164             this.executor = executor;
165         }
166 
167         pool = new IoProcessor[size];
168 
169         boolean success = false;
170         Constructor<? extends IoProcessor<S>> processorConstructor = null;
171         boolean usesExecutorArg = true;
172 
173         try {
174             // We create at least one processor
175             try {
176                 try {
177                     processorConstructor = processorType.getConstructor(ExecutorService.class);
178                     pool[0] = processorConstructor.newInstance(this.executor);
179                 } catch (NoSuchMethodException e1) {
180                     // To the next step...
181                     try {
182                         processorConstructor = processorType.getConstructor(Executor.class);
183                         pool[0] = processorConstructor.newInstance(this.executor);
184                     } catch (NoSuchMethodException e2) {
185                         // To the next step...
186                         try {
187                             processorConstructor = processorType.getConstructor();
188                             usesExecutorArg = false;
189                             pool[0] = processorConstructor.newInstance();
190                         } catch (NoSuchMethodException e3) {
191                             // To the next step...
192                         }
193                     }
194                 }
195             } catch (RuntimeException re) {
196                 LOGGER.error("Cannot create an IoProcessor :{}", re.getMessage());
197                 throw re;
198             } catch (Exception e) {
199                 String msg = "Failed to create a new instance of " + processorType.getName() + ":" + e.getMessage();
200                 LOGGER.error(msg, e);
201                 throw new RuntimeIoException(msg , e);
202             }
203 
204             if (processorConstructor == null) {
205                 // Raise an exception if no proper constructor is found.
206                 String msg = String.valueOf(processorType)
207                     + " must have a public constructor with one "
208                     + ExecutorService.class.getSimpleName()
209                     + " parameter, a public constructor with one "
210                     + Executor.class.getSimpleName()
211                     + " parameter or a public default constructor.";
212                 LOGGER.error(msg);
213                 throw new IllegalArgumentException(msg);
214             }
215 
216             // Constructor found now use it for all subsequent instantiations
217             for (int i = 1; i < pool.length; i++) {
218                 try {
219                     if (usesExecutorArg) {
220                         pool[i] = processorConstructor.newInstance(this.executor);
221                     } else {
222                         pool[i] = processorConstructor.newInstance();
223                     }
224                 } catch (Exception e) {
225                     // Won't happen because it has been done previously
226                 }
227             }
228             
229             success = true;
230         } finally {
231             if (!success) {
232                 dispose();
233             }
234         }
235     }
236 
237     /**
238      * {@inheritDoc}
239      */
240     public final void add(S session) {
241         getProcessor(session).add(session);
242     }
243 
244     /**
245      * {@inheritDoc}
246      */
247     public final void flush(S session) {
248         getProcessor(session).flush(session);
249     }
250 
251     /**
252      * {@inheritDoc}
253      */
254     public final void remove(S session) {
255         getProcessor(session).remove(session);
256     }
257 
258     /**
259      * {@inheritDoc}
260      */
261     public final void updateTrafficControl(S session) {
262         getProcessor(session).updateTrafficControl(session);
263     }
264 
265     /**
266      * {@inheritDoc}
267      */
268     public boolean isDisposed() {
269         return disposed;
270     }
271 
272     /**
273      * {@inheritDoc}
274      */
275     public boolean isDisposing() {
276         return disposing;
277     }
278 
279     /**
280      * {@inheritDoc}
281      */
282     public final void dispose() {
283         if (disposed) {
284             return;
285         }
286 
287         synchronized (disposalLock) {
288             if (!disposing) {
289                 disposing = true;
290                 
291                 for (IoProcessor<S> ioProcessor : pool) {
292                     if (ioProcessor == null) {
293                         // Special case if the pool has not been initialized properly
294                         continue;
295                     }
296                     
297                     if (ioProcessor.isDisposing()) {
298                         continue;
299                     }
300 
301                     try {
302                         ioProcessor.dispose();
303                     } catch (Exception e) {
304                         LOGGER.warn("Failed to dispose the {} IoProcessor.", ioProcessor.getClass().getSimpleName(), e);
305                     }
306                 }
307 
308                 if (createdExecutor) {
309                     ((ExecutorService) executor).shutdown();
310                 }
311             }
312 
313             Arrays.fill(pool, null);
314             disposed = true;
315         }
316     }
317 
318     /**
319      * Find the processor associated to a session. If it hasen't be stored into
320      * the session's attributes, pick a new processor and stores it.
321      */
322     @SuppressWarnings("unchecked")
323     private IoProcessor<S> getProcessor(S session) {
324         IoProcessor<S> processor = (IoProcessor<S>) session.getAttribute(PROCESSOR);
325         
326         if (processor == null) {
327             if (disposed || disposing) {
328                 throw new IllegalStateException("A disposed processor cannot be accessed.");
329             }
330 
331             processor = pool[Math.abs((int) session.getId()) % pool.length];
332             
333             if (processor == null) {
334                 throw new IllegalStateException("A disposed processor cannot be accessed.");
335             }
336             
337             session.setAttributeIfAbsent(PROCESSOR, processor);
338         }
339 
340         return processor;
341     }
342 }