001 /*
002 * Copyright 2010 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package com.hs.mail.container.server;
017
018 import java.io.IOException;
019 import java.net.InetAddress;
020 import java.net.ServerSocket;
021 import java.net.Socket;
022
023 import org.apache.commons.io.IOUtils;
024 import org.apache.commons.lang.StringUtils;
025 import org.apache.log4j.Logger;
026 import org.springframework.core.task.TaskExecutor;
027
028 import com.hs.mail.container.server.socket.ServerSocketFactory;
029 import com.hs.mail.container.server.socket.SocketConnection;
030
031 /**
032 *
033 * @author Won Chul Doh
034 * @since May 3, 2010
035 *
036 */
037 public class DefaultServer implements Server {
038
039 static Logger logger = Logger.getLogger(DefaultServer.class);
040
041 /**
042 * The port on which this server will be made available.
043 */
044 protected int port = -1;
045
046 /**
047 * The host name of bindTo address.
048 */
049 protected String bind = null;
050
051 /**
052 * Network interface to which the service will bind. If not set, the server
053 * binds to all available interfaces.
054 */
055 protected InetAddress bindTo = null;
056
057 /**
058 * The connection backlog.
059 */
060 protected int backlog = -1;
061
062 /**
063 * The connection idle timeout. Used primarily to prevent server problems
064 * from hanging a connection.
065 */
066 protected int connectionTimeout = 0;
067
068 /**
069 * Indicates whether or not TCP_NODELAY is enabled.
070 */
071 protected boolean tcpNoDelay = true;
072
073 /**
074 * Factory creating server sockets.
075 */
076 protected ServerSocketFactory serverSocketFactory = null;
077
078 /**
079 * The connection handler used by this service.
080 */
081 protected ConnectionHandler connectionHandler = null;
082
083 private TaskExecutor taskExecutor = null;
084
085 private MainSocketThread mainThread = null;
086
087 public int getPort() {
088 return port;
089 }
090
091 public void setPort(int port) {
092 this.port = port;
093 }
094
095 public String getBind() {
096 return bind;
097 }
098
099 public void setBind(String bind) {
100 this.bind = bind;
101 }
102
103 public InetAddress getBindTo() {
104 return bindTo;
105 }
106
107 public void setBindTo(InetAddress bindTo) {
108 this.bindTo = bindTo;
109 }
110
111 public int getBacklog() {
112 return backlog;
113 }
114
115 public void setBacklog(int backlog) {
116 this.backlog = backlog;
117 }
118
119 public int getConnectionTimeout() {
120 return connectionTimeout;
121 }
122
123 public void setConnectionTimeout(int connectionTimeout) {
124 this.connectionTimeout = connectionTimeout;
125 }
126
127 public boolean isTcpNoDelay() {
128 return tcpNoDelay;
129 }
130
131 public void setTcpNoDelay(boolean tcpNoDelay) {
132 this.tcpNoDelay = tcpNoDelay;
133 }
134
135 public ServerSocketFactory getServerSocketFactory() {
136 return serverSocketFactory;
137 }
138
139 public void setServerSocketFactory(ServerSocketFactory factory) {
140 this.serverSocketFactory = factory;
141 }
142
143 public ConnectionHandler getConnectionHandler() {
144 return connectionHandler;
145 }
146
147 public void setConnectionHandler(ConnectionHandler connectionHandler) {
148 this.connectionHandler = connectionHandler;
149 }
150
151 public TaskExecutor getTaskExecutor() {
152 return taskExecutor;
153 }
154
155 public void setTaskExecutor(TaskExecutor taskExecutor) {
156 this.taskExecutor = taskExecutor;
157 }
158
159 /**
160 * This method returns the type of service provided by this server.
161 * This should be invariant over the life of the class.
162 *
163 * Subclasses may override this implementation. This implementation
164 * parses the complete class name and returns the undecorated class
165 * name.
166 *
167 * @return description of this server
168 */
169 public String getServiceType() {
170 String name = getClass().getName();
171 int i = name.lastIndexOf(".");
172 if (i > 0 && i < name.length() - 2) {
173 name = name.substring(i + 1);
174 }
175 return name;
176 }
177
178 public void configure() throws Exception {
179 if (!StringUtils.isEmpty(bind) && !"*".equals(bind)) {
180 bindTo = InetAddress.getByName(bind);
181 }
182 }
183
184 public void start() {
185 mainThread = new MainSocketThread();
186 mainThread.start();
187
188 StringBuilder logBuffer = new StringBuilder(64)
189 .append(getServiceType())
190 .append(" started on port ")
191 .append(port);
192 Logger.getLogger("console").info(logBuffer.toString());
193 }
194
195 public void stop() {
196 mainThread.setCanContinue(false);
197 }
198
199 class MainSocketThread extends Thread {
200
201 protected boolean canContinue = true;
202 protected ServerSocket serverSocket = null;
203
204 public MainSocketThread() {
205 }
206
207 public synchronized void setCanContinue(boolean canContinue) {
208 this.canContinue = canContinue;
209 }
210
211 public void run() {
212 open();
213
214 if (null == this.serverSocket) {
215 return;
216 }
217
218 while (this.canContinue) {
219 Socket soc = null;
220
221 try {
222 soc = this.serverSocket.accept();
223 soc.setTcpNoDelay(tcpNoDelay);
224 soc.setSoTimeout(connectionTimeout);
225
226 SocketConnection sc = new SocketConnection(connectionHandler, soc);
227
228 try {
229 taskExecutor.execute(sc);
230 } catch (Exception e) {
231 if (logger.isDebugEnabled()) {
232 logger.error("Cannot add work into thread pool: " + e.getMessage(), e);
233 } else {
234 logger.error("Cannot add work into thread pool: " + e.getMessage());
235 }
236
237 if (soc != null) {
238 try {
239 soc.close();
240 } catch (Exception ce) {
241 }
242 }
243 }
244 } catch (IOException e) {
245 if (soc != null) {
246 try {
247 IOUtils.closeQuietly(soc.getInputStream ());
248 IOUtils.closeQuietly(soc.getOutputStream());
249 soc.close();
250 } catch (IOException ae) {
251 }
252 soc = null;
253 }
254
255 if (logger.isDebugEnabled()) {
256 logger.warn("IOException occurred: " + e.getMessage(), e);
257 } else {
258 logger.warn("IOException occurred: " + e.getMessage());
259 }
260
261 try {
262 synchronized (this) {
263 if (this.canContinue) {
264 if (logger.isInfoEnabled()) {
265 logger.info("Will reopen server socket.");
266 }
267 this.serverSocket.close();
268 open();
269 }
270 }
271 } catch (Exception oe) {
272 if (logger.isDebugEnabled()) {
273 logger.error("Cannot reopen server socket: " + oe.getMessage(), oe);
274 } else {
275 logger.error("Cannot reopen server socket: " + oe.getMessage());
276 }
277 break;
278 }
279 }
280 }
281 }
282
283 protected void open() {
284 if (null != bindTo) {
285 try {
286 this.serverSocket = serverSocketFactory.createServerSocket(port, backlog, bindTo);
287 } catch (IOException e) {
288 try {
289 if (backlog > 0) {
290 this.serverSocket = serverSocketFactory.createServerSocket(port, backlog);
291 } else {
292 this.serverSocket = serverSocketFactory.createServerSocket(port);
293 }
294 } catch (IOException ae) {
295 if (logger.isDebugEnabled()) {
296 logger.error("Cannot open server socket (" + port + "," + backlog + "):" + e.getMessage(), e);
297 } else {
298 logger.error("Cannot open server socket (" + port + "," + backlog + "):" + e.getMessage());
299 }
300 }
301 }
302 } else {
303 try {
304 if (backlog > 0) {
305 this.serverSocket = serverSocketFactory.createServerSocket(port, backlog);
306 } else {
307 this.serverSocket = serverSocketFactory.createServerSocket(port);
308 }
309 } catch (IOException e) {
310 if (logger.isDebugEnabled()) {
311 logger.error("Cannot open server socket (" + port + "," + backlog + "):" + e.getMessage(), e);
312 } else {
313 logger.error("Cannot open server socket (" + port + "," + backlog + "):" + e.getMessage());
314 }
315 }
316 }
317 }
318 }
319
320 }