View Javadoc

1   /*
2    *   @(#) $Id: DemuxingProtocolCodecFactory.java 210062 2005-07-11 03:52:38Z trustin $
3    *
4    *   Copyright 2004 The Apache Software Foundation
5    *
6    *   Licensed under the Apache License, Version 2.0 (the "License");
7    *   you may not use this file except in compliance with the License.
8    *   You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing, software
13   *   distributed under the License is distributed on an "AS IS" BASIS,
14   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *   See the License for the specific language governing permissions and
16   *   limitations under the License.
17   *
18   */
19  package org.apache.mina.protocol.codec;
20  
21  import java.util.HashSet;
22  import java.util.IdentityHashMap;
23  import java.util.Iterator;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.mina.common.ByteBuffer;
28  import org.apache.mina.protocol.ProtocolCodecFactory;
29  import org.apache.mina.protocol.ProtocolDecoder;
30  import org.apache.mina.protocol.ProtocolDecoderOutput;
31  import org.apache.mina.protocol.ProtocolEncoder;
32  import org.apache.mina.protocol.ProtocolEncoderOutput;
33  import org.apache.mina.protocol.ProtocolSession;
34  import org.apache.mina.protocol.ProtocolViolationException;
35  
36  /***
37   * A composite {@link ProtocolCodecFactory} that consists of multiple
38   * {@link MessageEncoder}s and {@link MessageDecoder}s.
39   * {@link ProtocolEncoder} and {@link ProtocolDecoder} this factory
40   * returns demultiplex incoming messages and buffers to
41   * appropriate {@link MessageEncoder}s and {@link MessageDecoder}s. 
42   * 
43   * @author The Apache Directory Project (dev@directory.apache.org)
44   * @author Trustin Lee (trustin@apache.org)
45   * @version $Rev: 210062 $, $Date: 2005-07-11 12:52:38 +0900 $
46   * 
47   * @see MessageEncoder
48   * @see MessageDecoder
49   */
50  public class DemuxingProtocolCodecFactory implements ProtocolCodecFactory {
51  
52      private MessageDecoderFactory[] decoderFactories = new MessageDecoderFactory[0];
53      private MessageEncoderFactory[] encoderFactories = new MessageEncoderFactory[0];
54      
55      public DemuxingProtocolCodecFactory()
56      {
57      }
58      
59      public void register( Class encoderOrDecoderClass )
60      {
61          if( encoderOrDecoderClass == null )
62          {
63              throw new NullPointerException( "encoderOrDecoderClass" );
64          }
65  
66          boolean registered = false;
67          if( MessageEncoder.class.isAssignableFrom( encoderOrDecoderClass ) )
68          {
69              register( new DefaultConstructorMessageEncoderFactory( encoderOrDecoderClass ) );
70              registered = true;
71          }
72          
73          if( MessageDecoder.class.isAssignableFrom( encoderOrDecoderClass ) )
74          {
75              register( new DefaultConstructorMessageDecoderFactory( encoderOrDecoderClass ) );
76              registered = true;
77          }
78          
79          if( !registered )
80          {
81              throw new IllegalArgumentException( "Unregisterable type: " + encoderOrDecoderClass );
82          }
83      }
84      
85      public void register( MessageEncoder encoder )
86      {
87          register( new SingletonMessageEncoderFactory( encoder ) );
88      }
89      
90      public void register( MessageEncoderFactory factory )
91      {
92          if( factory == null )
93          {
94              throw new NullPointerException( "factory" );
95          }
96          MessageEncoderFactory[] encoderFactories = this.encoderFactories;
97          MessageEncoderFactory[] newEncoderFactories = new MessageEncoderFactory[ encoderFactories.length + 1 ];
98          System.arraycopy( encoderFactories, 0, newEncoderFactories, 0, encoderFactories.length );
99          newEncoderFactories[ encoderFactories.length ] = factory;
100         this.encoderFactories = newEncoderFactories;
101     }
102     
103     public void register( MessageDecoder decoder )
104     {
105         register( new SingletonMessageDecoderFactory( decoder ) );
106     }
107     
108     public void register( MessageDecoderFactory factory )
109     {
110         if( factory == null )
111         {
112             throw new NullPointerException( "factory" );
113         }
114         MessageDecoderFactory[] decoderFactories = this.decoderFactories;
115         MessageDecoderFactory[] newDecoderFactories = new MessageDecoderFactory[ decoderFactories.length + 1 ];
116         System.arraycopy( decoderFactories, 0, newDecoderFactories, 0, decoderFactories.length );
117         newDecoderFactories[ decoderFactories.length ] = factory;
118         this.decoderFactories = newDecoderFactories;
119     }
120     
121     public ProtocolEncoder newEncoder() {
122         return new ProtocolEncoderImpl();
123     }
124 
125     public ProtocolDecoder newDecoder() {
126         return new ProtocolDecoderImpl();
127     }
128     
129     private class ProtocolEncoderImpl implements ProtocolEncoder
130     {
131         private final Map encoders = new IdentityHashMap();
132         
133         private ProtocolEncoderImpl()
134         {
135             MessageEncoderFactory[] encoderFactories = DemuxingProtocolCodecFactory.this.encoderFactories;
136             for( int i = encoderFactories.length - 1; i >= 0; i-- )
137             {
138                 MessageEncoder encoder = encoderFactories[ i ].newEncoder();
139                 Iterator it = encoder.getMessageTypes().iterator();
140                 while( it.hasNext() )
141                 {
142                     Class type = ( Class ) it.next();
143                     encoders.put( type, encoder );
144                 }
145             }
146         }
147         
148         public void encode( ProtocolSession session, Object message,
149                             ProtocolEncoderOutput out ) throws ProtocolViolationException
150         {
151             Class type = message.getClass();
152             MessageEncoder encoder = findEncoder( type );
153             if( encoder == null )
154             {
155                 throw new ProtocolViolationException( "Unexpected message type: " + type );
156             }
157             
158             encoder.encode( session, message, out );
159         }
160         
161         private MessageEncoder findEncoder( Class type )
162         {
163             MessageEncoder encoder = ( MessageEncoder ) encoders.get( type );
164             if( encoder == null )
165             {
166                 encoder = findEncoder( type, new HashSet() );
167             }
168 
169             return encoder;
170         }
171 
172         private MessageEncoder findEncoder( Class type, Set triedClasses )
173         {
174             MessageEncoder encoder;
175 
176             if( triedClasses.contains( type ) )
177                 return null;
178             triedClasses.add( type );
179 
180             encoder = ( MessageEncoder ) encoders.get( type );
181             if( encoder == null )
182             {
183                 encoder = findEncoder( type, triedClasses );
184                 if( encoder != null )
185                     return encoder;
186 
187                 Class[] interfaces = type.getInterfaces();
188                 for( int i = 0; i < interfaces.length; i ++ )
189                 {
190                     encoder = findEncoder( interfaces[ i ], triedClasses );
191                     if( encoder != null )
192                         return encoder;
193                 }
194 
195                 return null;
196             }
197             else
198                 return encoder;
199         }
200     }
201     
202     private class ProtocolDecoderImpl extends CumulativeProtocolDecoder
203     {
204         private final MessageDecoder[] decoders;
205         private MessageDecoder currentDecoder;
206 
207         protected ProtocolDecoderImpl()
208         {
209             super( 16 );
210             
211             MessageDecoderFactory[] decoderFactories = DemuxingProtocolCodecFactory.this.decoderFactories;
212             decoders = new MessageDecoder[ decoderFactories.length ];
213             for( int i = decoderFactories.length - 1; i >= 0; i-- )
214             {
215                 decoders[ i ] = decoderFactories[ i ].newDecoder();
216             }
217         }
218 
219         protected boolean doDecode( ProtocolSession session, ByteBuffer in,
220                                     ProtocolDecoderOutput out) throws ProtocolViolationException
221         {
222             if( currentDecoder == null )
223             {
224                 MessageDecoder[] decoders = this.decoders;
225                 int undecodables = 0;
226                 for( int i = decoders.length - 1; i >= 0; i -- ) 
227                 {
228                     MessageDecoder decoder = decoders[i];
229                     int limit = in.limit();
230                     int pos = in.position();
231 
232                     MessageDecoderResult result;
233                     try
234                     {
235                         result = decoder.decodable( session, in );
236                     }
237                     finally
238                     {
239                         in.position( pos );
240                         in.limit( limit );
241                     }
242                     
243                     if( result == MessageDecoder.OK )
244                     {
245                         currentDecoder = decoder;
246                         break;
247                     }
248                     else if( result == MessageDecoder.NOT_OK )
249                     {
250                         undecodables ++;
251                     }
252                     else if( result != MessageDecoder.NEED_DATA )
253                     {
254                         throw new IllegalStateException( "Unexpected decode result (see your decodable()): " + result );
255                     }
256                 }
257                 
258                 if( undecodables == decoders.length )
259                 {
260                     // Throw an exception if all decoders cannot decode data.
261                     in.position( in.limit() ); // Skip data
262                     throw new ProtocolViolationException(
263                             "No appropriate message decoder: " + in.getHexDump() );
264                 }
265                 
266                 if( currentDecoder == null )
267                 {
268                     // Decoder is not determined yet (i.e. we need more data)
269                     return false;
270                 }
271             }
272             
273             MessageDecoderResult result = currentDecoder.decode( session, in, out );
274             if( result == MessageDecoder.OK )
275             {
276                 currentDecoder = null;
277                 return true;
278             }
279             else if( result == MessageDecoder.NEED_DATA )
280             {
281                 return false;
282             }
283             else if( result == MessageDecoder.NOT_OK )
284             {
285                 throw new ProtocolViolationException( "Message decoder returned NOT_OK." );
286             }
287             else
288             {
289                 throw new IllegalStateException( "Unexpected decode result (see your decode()): " + result );
290             }
291         }
292     }
293     
294     private static class SingletonMessageEncoderFactory implements MessageEncoderFactory
295     {
296         private final MessageEncoder encoder;
297         
298         private SingletonMessageEncoderFactory( MessageEncoder encoder )
299         {
300             if( encoder == null )
301             {
302                 throw new NullPointerException( "encoder" );
303             }
304             this.encoder = encoder;
305         }
306 
307         public MessageEncoder newEncoder()
308         {
309             return encoder;
310         }
311     }
312 
313     private static class SingletonMessageDecoderFactory implements MessageDecoderFactory
314     {
315         private final MessageDecoder decoder;
316         
317         private SingletonMessageDecoderFactory( MessageDecoder decoder )
318         {
319             if( decoder == null )
320             {
321                 throw new NullPointerException( "decoder" );
322             }
323             this.decoder = decoder;
324         }
325 
326         public MessageDecoder newDecoder()
327         {
328             return decoder;
329         }
330     }
331     
332     private static class DefaultConstructorMessageEncoderFactory implements MessageEncoderFactory
333     {
334         private final Class encoderClass;
335         
336         private DefaultConstructorMessageEncoderFactory( Class encoderClass )
337         {
338             if( encoderClass == null )
339             {
340                 throw new NullPointerException( "encoderClass" );
341             }
342             
343             if( !MessageEncoder.class.isAssignableFrom( encoderClass ) )
344             {
345                 throw new IllegalArgumentException( "encoderClass is not assignable to MessageEncoder" );
346             }
347             this.encoderClass = encoderClass;
348         }
349 
350         public MessageEncoder newEncoder()
351         {
352             try
353             {
354                 return ( MessageEncoder ) encoderClass.newInstance();
355             }
356             catch( Exception e )
357             {
358                 throw new RuntimeException( "Failed to create a new instance of " + encoderClass, e );
359             }
360         }
361     }
362 
363     private static class DefaultConstructorMessageDecoderFactory implements MessageDecoderFactory
364     {
365         private final Class decoderClass;
366         
367         private DefaultConstructorMessageDecoderFactory( Class decoderClass )
368         {
369             if( decoderClass == null )
370             {
371                 throw new NullPointerException( "decoderClass" );
372             }
373             
374             if( !MessageDecoder.class.isAssignableFrom( decoderClass ) )
375             {
376                 throw new IllegalArgumentException( "decoderClass is not assignable to MessageDecoder" );
377             }
378             this.decoderClass = decoderClass;
379         }
380 
381         public MessageDecoder newDecoder()
382         {
383             try
384             {
385                 return ( MessageDecoder ) decoderClass.newInstance();
386             }
387             catch( Exception e )
388             {
389                 throw new RuntimeException( "Failed to create a new instance of " + decoderClass, e );
390             }
391         }
392     }
393 }