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    }