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