View Javadoc

1   /*** 
2    * 
3    * Copyright 2004 Hiram Chirino
4    * 
5    * Licensed under the Apache License, Version 2.0 (the "License"); 
6    * you may not use this file except in compliance with the License. 
7    * You may obtain a copy of the License at 
8    * 
9    * http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, 
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
14   * See the License for the specific language governing permissions and 
15   * limitations under the License. 
16   * 
17   **/
18  package org.activeio.oneport;
19  
20  import java.io.IOException;
21  import java.net.URI;
22  import java.util.Iterator;
23  
24  import org.activeio.AcceptListener;
25  import org.activeio.AsynchChannel;
26  import org.activeio.AsynchChannelListener;
27  import org.activeio.AsynchChannelServer;
28  import org.activeio.Channel;
29  import org.activeio.FilterAsynchChannel;
30  import org.activeio.FilterAsynchChannelServer;
31  import org.activeio.Packet;
32  import org.activeio.SynchChannel;
33  import org.activeio.adapter.AsynchToSynchChannelAdapter;
34  import org.activeio.adapter.SynchToAsynchChannelAdapter;
35  import org.activeio.filter.PushbackSynchChannel;
36  import org.activeio.packet.AppendedPacket;
37  
38  import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
39  
40  /***
41   * Allows multiple protocols share a single ChannelServer.  All protocols sharing the server 
42   * must have a distinct magic number at the beging of the client's request.
43   * 
44   * TODO: handle the case where a client opens a connection but sends no data down the stream.  We need
45   * to timeout that client.
46   * 
47   * @version $Revision$
48   */
49  final public class OnePortAsynchChannelServer extends FilterAsynchChannelServer {
50      
51      /***
52       * The OnePortAsynchChannelServer listens for incoming connection
53       * from a normal AsynchChannelServer.  This s the listner used 
54       * to receive the accepted channels.
55       */
56      final private class OnePortAcceptListener implements AcceptListener {
57          
58          public void onAccept(Channel channel) {
59              try {
60                  AsynchChannel asynchChannel = SynchToAsynchChannelAdapter.adapt(channel);
61                  ProtocolInspectingAsynchChannel inspector = new ProtocolInspectingAsynchChannel(asynchChannel);
62                  inspector.start();                
63              } catch (IOException e) {
64                  onAcceptError(e);
65              }                
66          }
67          
68          public void onAcceptError(IOException error) {
69              dispose();
70          }
71      }
72  
73      /***
74       * This channel filter sniffs the first few bytes of the byte stream 
75       * to see if a ProtocolRecognizer recognizes the protocol.  If it does not
76       * it just closes the channel, otherwise the associated SubPortAsynchChannelServer
77       * is notified that it accepted a channel.
78       *
79       */
80      final private class ProtocolInspectingAsynchChannel extends FilterAsynchChannel {
81          private Packet buffer;
82  
83          public ProtocolInspectingAsynchChannel(AsynchChannel next) throws IOException {
84              super(next);
85              setAsynchChannelListener(new AsynchChannelListener() {
86                  public void onPacket(Packet packet) {
87                      if (buffer == null) {
88                          buffer = packet;
89                      } else {
90                          buffer = AppendedPacket.join(buffer, packet);
91                      }
92                      findMagicNumber();
93                  }
94  
95                  public void onPacketError(IOException error) {
96                      dispose();
97                  }
98              });
99          }
100 
101         private void findMagicNumber() {
102             for (Iterator iter = recognizerMap.keySet().iterator(); iter.hasNext();) {
103                 ProtocolRecognizer recognizer = (ProtocolRecognizer) iter.next();
104                 if (recognizer.recognizes(buffer.duplicate())) {
105 
106                     if( UnknownRecognizer.UNKNOWN_RECOGNIZER == recognizer ) {
107                         // Dispose the channel.. don't know what to do with it.
108                         dispose();
109                     }
110                     
111                     SubPortAsynchChannelServer onePort = (SubPortAsynchChannelServer) recognizerMap.get(recognizer);
112                     if( onePort == null ) {
113                         // Dispose the channel.. don't know what to do with it.
114                         dispose();
115                     }
116 
117                     // Once the magic number is found:
118                     // Stop the channel so that a decision can be taken on what to
119                     // do with the
120                     // channel. When the channel is restarted, the buffered up
121                     // packets wiil get
122                     // delivered.
123                     try {
124                         stop(NO_WAIT_TIMEOUT);
125                         setAsynchChannelListener(null);
126                     } catch (IOException e) {                        
127                         getAsynchChannelListener().onPacketError(e);
128                     }
129                     
130                     Channel channel = getNext();
131                     channel = AsynchToSynchChannelAdapter.adapt(channel);
132                     channel = new PushbackSynchChannel((SynchChannel) channel, buffer);
133                     channel = SynchToAsynchChannelAdapter.adapt(channel);
134                     
135                     onePort.onAccept(channel);
136                     break;
137                 }
138             }
139         }
140     }    
141 
142     /***
143      * Clients bind against the OnePortAsynchChannelServer and get 
144      * SubPortAsynchChannelServer which can be used to accept connections.
145      */
146     final private class SubPortAsynchChannelServer implements AsynchChannelServer {
147         
148         private final ProtocolRecognizer recognizer;
149         private AcceptListener acceptListener;
150         private boolean started;
151         
152         /***
153          * @param recognizer
154          */
155         public SubPortAsynchChannelServer(ProtocolRecognizer recognizer) {
156             this.recognizer = recognizer;
157         }
158 
159         public void setAcceptListener(AcceptListener acceptListener) {
160             this.acceptListener = acceptListener;
161         }
162         
163         public URI getBindURI() {
164             return next.getBindURI();
165         }
166         
167         public URI getConnectURI() {
168             return next.getConnectURI();
169         }
170         
171         public void dispose() {
172             started = false;
173             recognizerMap.remove(recognizer);
174         }
175         
176         public void start() throws IOException {
177             started = true;
178         }
179         public void stop(long timeout) throws IOException {
180             started = false;
181         }
182         
183         void onAccept(Channel channel) {
184             if( started && acceptListener!=null ) {
185                 acceptListener.onAccept(channel);
186             } else {
187                 // Dispose the channel.. don't know what to do with it.
188                 channel.dispose();
189             }
190         }
191         
192         public Object narrow(Class target) {
193             if( target.isAssignableFrom(getClass()) ) {
194                 return this;
195             }
196             return OnePortAsynchChannelServer.this.narrow(target);
197         }    
198         
199     }
200     
201     
202     private final ConcurrentHashMap recognizerMap = new ConcurrentHashMap();
203 
204     public OnePortAsynchChannelServer(AsynchChannelServer server) throws IOException {
205         super(server);
206         super.setAcceptListener(new OnePortAcceptListener());
207     }
208     
209     public void setAcceptListener(AcceptListener acceptListener) {
210         throw new IllegalAccessError("Not supported");
211     }    
212     
213     public AsynchChannelServer bindAsynchChannel(ProtocolRecognizer recognizer) throws IOException {
214         
215         if( recognizerMap.contains(recognizer) ) 
216             throw new IOException("That recognizer is allredy bound.");
217         
218         SubPortAsynchChannelServer server = new SubPortAsynchChannelServer(recognizer);
219         Object old = recognizerMap.put(recognizer, server);
220         return server;
221     }
222     
223     
224 }