1   // ========================================================================
2   // Copyright 2003-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14   
15  package org.mortbay.jetty.bio;
16  
17  import java.io.IOException;
18  import java.net.InetAddress;
19  import java.net.ServerSocket;
20  import java.net.Socket;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.Set;
24  
25  import org.mortbay.io.Buffer;
26  import org.mortbay.io.ByteArrayBuffer;
27  import org.mortbay.io.EndPoint;
28  import org.mortbay.io.bio.SocketEndPoint;
29  import org.mortbay.jetty.AbstractConnector;
30  import org.mortbay.jetty.EofException;
31  import org.mortbay.jetty.HttpConnection;
32  import org.mortbay.jetty.HttpException;
33  import org.mortbay.jetty.Request;
34  import org.mortbay.log.Log;
35  
36  
37  /* ------------------------------------------------------------------------------- */
38  /**  Socket Connector.
39   * This connector implements a traditional blocking IO and threading model.
40   * Normal JRE sockets are used and a thread is allocated per connection.
41   * Buffers are managed so that large buffers are only allocated to active connections.
42   * 
43   * This Connector should only be used if NIO is not available.
44   * 
45   * @org.apache.xbean.XBean element="bioConnector" description="Creates a BIO based socket connector"
46   * 
47   * @author gregw
48   */
49  public class SocketConnector extends AbstractConnector
50  {
51      protected ServerSocket _serverSocket;
52      protected Set _connections;
53      
54      /* ------------------------------------------------------------ */
55      /** Constructor.
56       * 
57       */
58      public SocketConnector()
59      {
60      }
61  
62      /* ------------------------------------------------------------ */
63      public Object getConnection()
64      {
65          return _serverSocket;
66      }
67      
68      /* ------------------------------------------------------------ */
69      public void open() throws IOException
70      {
71          // Create a new server socket and set to non blocking mode
72          if (_serverSocket==null || _serverSocket.isClosed())
73          _serverSocket= newServerSocket(getHost(),getPort(),getAcceptQueueSize());
74          _serverSocket.setReuseAddress(getReuseAddress());
75      }
76  
77      /* ------------------------------------------------------------ */
78      protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException
79      {
80          ServerSocket ss= host==null?
81              new ServerSocket(port,backlog):
82              new ServerSocket(port,backlog,InetAddress.getByName(host));
83         
84          return ss;
85      }
86      
87      /* ------------------------------------------------------------ */
88      public void close() throws IOException
89      {
90          if (_serverSocket!=null)
91              _serverSocket.close();
92          _serverSocket=null;
93      }
94  
95      /* ------------------------------------------------------------ */
96      public void accept(int acceptorID)
97      	throws IOException, InterruptedException
98      {   
99          Socket socket = _serverSocket.accept();
100         configure(socket);
101         
102         Connection connection=new Connection(socket);
103         connection.dispatch();
104     }
105 
106     /* ------------------------------------------------------------------------------- */
107     /**
108      * Allows subclass to override Conection if required.
109      */
110     protected HttpConnection newHttpConnection(EndPoint endpoint) 
111     {
112         return new HttpConnection(this, endpoint, getServer());
113     }
114 
115     /* ------------------------------------------------------------------------------- */
116     protected Buffer newBuffer(int size)
117     {
118         return new ByteArrayBuffer(size);
119     }
120 
121     /* ------------------------------------------------------------------------------- */
122     public void customize(EndPoint endpoint, Request request)
123         throws IOException
124     {
125         Connection connection = (Connection)endpoint;
126         if (connection._sotimeout!=_maxIdleTime)
127         {
128             connection._sotimeout=_maxIdleTime;
129             ((Socket)endpoint.getTransport()).setSoTimeout(_maxIdleTime);
130         }
131               
132         super.customize(endpoint, request);
133     }
134 
135     /* ------------------------------------------------------------------------------- */
136     public int getLocalPort()
137     {
138         if (_serverSocket==null || _serverSocket.isClosed())
139             return -1;
140         return _serverSocket.getLocalPort();
141     }
142 
143     /* ------------------------------------------------------------------------------- */
144     protected void doStart() throws Exception
145     {
146         _connections=new HashSet();
147         super.doStart();
148     }
149 
150     /* ------------------------------------------------------------------------------- */
151     protected void doStop() throws Exception
152     {
153         super.doStop();
154         Set set=null;
155 
156         synchronized(_connections)
157         {
158             set= new HashSet(_connections);
159         }
160         
161         Iterator iter=set.iterator();
162         while(iter.hasNext())
163         {
164             Connection connection = (Connection)iter.next();
165             connection.close();
166         }
167     }
168 
169     /* ------------------------------------------------------------------------------- */
170     /* ------------------------------------------------------------------------------- */
171     /* ------------------------------------------------------------------------------- */
172     protected class Connection extends SocketEndPoint implements Runnable
173     {
174         boolean _dispatched=false;
175         HttpConnection _connection;
176         int _sotimeout;
177         protected Socket _socket;
178         
179         public Connection(Socket socket) throws IOException
180         {
181             super(socket);
182             _connection = newHttpConnection(this);
183             _sotimeout=socket.getSoTimeout();
184             _socket=socket;
185         }
186         
187         public void dispatch() throws InterruptedException, IOException
188         {
189             if (!getThreadPool().dispatch(this))
190             {
191                 Log.warn("dispatch failed for {}",_connection);
192                 close();
193             }
194         }
195         
196         public int fill(Buffer buffer) throws IOException
197         {
198             int l = super.fill(buffer);
199             if (l<0)
200                 close();
201             return l;
202         }
203         
204         
205         public void close() throws IOException
206         {
207             _connection.getRequest().reset();
208             super.close();
209         }
210 
211         public void run()
212         {
213             try
214             {
215                 connectionOpened(_connection);
216                 synchronized(_connections)
217                 {
218                     _connections.add(this);
219                 }
220                 
221                 while (isStarted() && !isClosed())
222                 {
223                     if (_connection.isIdle())
224                     {
225                         if (getServer().getThreadPool().isLowOnThreads())
226                         {
227                             int lrmit = getLowResourceMaxIdleTime();
228                             if (lrmit>=0 && _sotimeout!= lrmit)
229                             {
230                                 _sotimeout=lrmit;
231                                 _socket.setSoTimeout(_sotimeout);
232                             }
233                         }
234                     }                    
235                     _connection.handle();
236                 }
237             }
238             catch (EofException e)
239             {
240                 Log.debug("EOF", e);
241                 try{close();}
242                 catch(IOException e2){Log.ignore(e2);}
243             }
244             catch (HttpException e)
245             {
246                 Log.debug("BAD", e);
247                 try{close();}
248                 catch(IOException e2){Log.ignore(e2);}
249             }
250             catch(Exception e)
251             {
252                 Log.warn("handle failed?",e);
253                 try{close();}
254                 catch(IOException e2){Log.ignore(e2);}
255             }
256             finally
257             { 
258                 connectionClosed(_connection);
259                 synchronized(_connections)
260                 {
261                     _connections.remove(this);
262                 }
263             }
264         }
265     }
266 }