View Javadoc
1   /*
2    * Copyright 2012 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  package org.jboss.netty.channel.socket.oio;
17  
18  import java.util.concurrent.Executor;
19  import java.util.concurrent.Executors;
20  import java.util.concurrent.RejectedExecutionException;
21  
22  import org.jboss.netty.channel.Channel;
23  import org.jboss.netty.channel.ChannelPipeline;
24  import org.jboss.netty.channel.group.ChannelGroup;
25  import org.jboss.netty.channel.socket.ClientSocketChannelFactory;
26  import org.jboss.netty.channel.socket.SocketChannel;
27  import org.jboss.netty.util.ThreadNameDeterminer;
28  import org.jboss.netty.util.internal.ExecutorUtil;
29  
30  /**
31   * A {@link ClientSocketChannelFactory} which creates a client-side blocking
32   * I/O based {@link SocketChannel}.  It utilizes the good old blocking I/O API
33   * which is known to yield better throughput and latency when there are
34   * relatively small number of connections to serve.
35   *
36   * <h3>How threads work</h3>
37   * <p>
38   * There is only one type of threads in {@link OioClientSocketChannelFactory};
39   * worker threads.
40   *
41   * <h4>Worker threads</h4>
42   * <p>
43   * Each connected {@link Channel} has a dedicated worker thread, just like a
44   * traditional blocking I/O thread model.
45   *
46   * <h3>Life cycle of threads and graceful shutdown</h3>
47   * <p>
48   * Worker threads are acquired from the {@link Executor} which was specified
49   * when a {@link OioClientSocketChannelFactory} was created (i.e. {@code workerExecutor}.)
50   * Therefore, you should make sure the specified {@link Executor} is able to
51   * lend the sufficient number of threads.
52   * <p>
53   * Worker threads are acquired lazily, and then released when there's nothing
54   * left to process.  All the related resources are also released when the
55   * worker threads are released.  Therefore, to shut down a service gracefully,
56   * you should do the following:
57   *
58   * <ol>
59   * <li>close all channels created by the factory usually using
60   *     {@link ChannelGroup#close()}, and</li>
61   * <li>call {@link #releaseExternalResources()}.</li>
62   * </ol>
63   *
64   * Please make sure not to shut down the executor until all channels are
65   * closed.  Otherwise, you will end up with a {@link RejectedExecutionException}
66   * and the related resources might not be released properly.
67   *
68   * <h3>Limitation</h3>
69   * <p>
70   * A {@link SocketChannel} created by this factory does not support asynchronous
71   * operations.  Any I/O requests such as {@code "connect"} and {@code "write"}
72   * will be performed in a blocking manner.
73   *
74   * @apiviz.landmark
75   */
76  public class OioClientSocketChannelFactory implements ClientSocketChannelFactory {
77  
78      private final Executor workerExecutor;
79      final OioClientSocketPipelineSink sink;
80      private boolean shutdownExecutor;
81  
82      /**
83       * Creates a new instance with a {@link Executors#newCachedThreadPool()} as worker executor.
84       *
85       * See {@link #OioClientSocketChannelFactory(Executor)}
86       */
87      public OioClientSocketChannelFactory() {
88          this(Executors.newCachedThreadPool());
89          shutdownExecutor = true;
90      }
91  
92      /**
93       * Creates a new instance.
94       *
95       * @param workerExecutor
96       *        the {@link Executor} which will execute the I/O worker threads
97       */
98      public OioClientSocketChannelFactory(Executor workerExecutor) {
99          this(workerExecutor, null);
100     }
101 
102     /**
103      * Creates a new instance.
104      *
105      * @param workerExecutor
106      *        the {@link Executor} which will execute the I/O worker threads
107      * @param determiner
108      *        the {@link ThreadNameDeterminer} to set the thread names.
109      */
110     public OioClientSocketChannelFactory(Executor workerExecutor,
111                                          ThreadNameDeterminer determiner) {
112         if (workerExecutor == null) {
113             throw new NullPointerException("workerExecutor");
114         }
115         this.workerExecutor = workerExecutor;
116         sink = new OioClientSocketPipelineSink(workerExecutor, determiner);
117     }
118 
119     public SocketChannel newChannel(ChannelPipeline pipeline) {
120         return new OioClientSocketChannel(this, pipeline, sink);
121     }
122 
123     public void shutdown() {
124         if (shutdownExecutor) {
125             ExecutorUtil.shutdownNow(workerExecutor);
126         }
127     }
128 
129     public void releaseExternalResources() {
130         ExecutorUtil.shutdownNow(workerExecutor);
131     }
132 }