View Javadoc

1   /*
2    *   @(#) $Id: DemuxingProtocolCodecFactory.java 327113 2005-10-21 06:59:15Z 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   * @version $Rev: 327113 $, $Date: 2005-10-21 15:59:15 +0900 $
45   * 
46   * @see MessageEncoder
47   * @see MessageDecoder
48   */
49  public class DemuxingProtocolCodecFactory implements ProtocolCodecFactory {
50  
51      private MessageDecoderFactory[] decoderFactories = new MessageDecoderFactory[0];
52      private MessageEncoderFactory[] encoderFactories = new MessageEncoderFactory[0];
53      
54      public DemuxingProtocolCodecFactory()
55      {
56      }
57      
58      public void register( Class encoderOrDecoderClass )
59      {
60          if( encoderOrDecoderClass == null )
61          {
62              throw new NullPointerException( "encoderOrDecoderClass" );
63          }
64  
65          boolean registered = false;
66          if( MessageEncoder.class.isAssignableFrom( encoderOrDecoderClass ) )
67          {
68              register( new DefaultConstructorMessageEncoderFactory( encoderOrDecoderClass ) );
69              registered = true;
70          }
71          
72          if( MessageDecoder.class.isAssignableFrom( encoderOrDecoderClass ) )
73          {
74              register( new DefaultConstructorMessageDecoderFactory( encoderOrDecoderClass ) );
75              registered = true;
76          }
77          
78          if( !registered )
79          {
80              throw new IllegalArgumentException( "Unregisterable type: " + encoderOrDecoderClass );
81          }
82      }
83      
84      public void register( MessageEncoder encoder )
85      {
86          register( new SingletonMessageEncoderFactory( encoder ) );
87      }
88      
89      public void register( MessageEncoderFactory factory )
90      {
91          if( factory == null )
92          {
93              throw new NullPointerException( "factory" );
94          }
95          MessageEncoderFactory[] encoderFactories = this.encoderFactories;
96          MessageEncoderFactory[] newEncoderFactories = new MessageEncoderFactory[ encoderFactories.length + 1 ];
97          System.arraycopy( encoderFactories, 0, newEncoderFactories, 0, encoderFactories.length );
98          newEncoderFactories[ encoderFactories.length ] = factory;
99          this.encoderFactories = newEncoderFactories;
100     }
101     
102     public void register( MessageDecoder decoder )
103     {
104         register( new SingletonMessageDecoderFactory( decoder ) );
105     }
106     
107     public void register( MessageDecoderFactory factory )
108     {
109         if( factory == null )
110         {
111             throw new NullPointerException( "factory" );
112         }
113         MessageDecoderFactory[] decoderFactories = this.decoderFactories;
114         MessageDecoderFactory[] newDecoderFactories = new MessageDecoderFactory[ decoderFactories.length + 1 ];
115         System.arraycopy( decoderFactories, 0, newDecoderFactories, 0, decoderFactories.length );
116         newDecoderFactories[ decoderFactories.length ] = factory;
117         this.decoderFactories = newDecoderFactories;
118     }
119     
120     public ProtocolEncoder newEncoder() {
121         return new ProtocolEncoderImpl();
122     }
123 
124     public ProtocolDecoder newDecoder() {
125         return new ProtocolDecoderImpl();
126     }
127     
128     private class ProtocolEncoderImpl implements ProtocolEncoder
129     {
130         private final Map encoders = new IdentityHashMap();
131         
132         private ProtocolEncoderImpl()
133         {
134             MessageEncoderFactory[] encoderFactories = DemuxingProtocolCodecFactory.this.encoderFactories;
135             for( int i = encoderFactories.length - 1; i >= 0; i-- )
136             {
137                 MessageEncoder encoder = encoderFactories[ i ].newEncoder();
138                 Iterator it = encoder.getMessageTypes().iterator();
139                 while( it.hasNext() )
140                 {
141                     Class type = ( Class ) it.next();
142                     encoders.put( type, encoder );
143                 }
144             }
145         }
146         
147         public void encode( ProtocolSession session, Object message,
148                             ProtocolEncoderOutput out ) throws ProtocolViolationException
149         {
150             Class type = message.getClass();
151             MessageEncoder encoder = findEncoder( type );
152             if( encoder == null )
153             {
154                 throw new ProtocolViolationException( "Unexpected message type: " + type );
155             }
156             
157             encoder.encode( session, message, out );
158         }
159         
160         private MessageEncoder findEncoder( Class type )
161         {
162             MessageEncoder encoder = ( MessageEncoder ) encoders.get( type );
163             if( encoder == null )
164             {
165                 encoder = findEncoder( type, new HashSet() );
166             }
167 
168             return encoder;
169         }
170 
171         private MessageEncoder findEncoder( Class type, Set triedClasses )
172         {
173             MessageEncoder encoder;
174 
175             if( triedClasses.contains( type ) )
176                 return null;
177             triedClasses.add( type );
178 
179             encoder = ( MessageEncoder ) encoders.get( type );
180             if( encoder == null )
181             {
182                 encoder = findEncoder( type, triedClasses );
183                 if( encoder != null )
184                     return encoder;
185 
186                 Class[] interfaces = type.getInterfaces();
187                 for( int i = 0; i < interfaces.length; i ++ )
188                 {
189                     encoder = findEncoder( interfaces[ i ], triedClasses );
190                     if( encoder != null )
191                         return encoder;
192                 }
193 
194                 return null;
195             }
196             else
197                 return encoder;
198         }
199     }
200     
201     private class ProtocolDecoderImpl extends CumulativeProtocolDecoder
202     {
203         private final MessageDecoder[] decoders;
204         private MessageDecoder currentDecoder;
205 
206         protected ProtocolDecoderImpl()
207         {
208             super( 16 );
209             
210             MessageDecoderFactory[] decoderFactories = DemuxingProtocolCodecFactory.this.decoderFactories;
211             decoders = new MessageDecoder[ decoderFactories.length ];
212             for( int i = decoderFactories.length - 1; i >= 0; i-- )
213             {
214                 decoders[ i ] = decoderFactories[ i ].newDecoder();
215             }
216         }
217 
218         protected boolean doDecode( ProtocolSession session, ByteBuffer in,
219                                     ProtocolDecoderOutput out) throws ProtocolViolationException
220         {
221             if( currentDecoder == null )
222             {
223                 MessageDecoder[] decoders = this.decoders;
224                 int undecodables = 0;
225                 for( int i = decoders.length - 1; i >= 0; i -- ) 
226                 {
227                     MessageDecoder decoder = decoders[i];
228                     int limit = in.limit();
229                     int pos = in.position();
230 
231                     MessageDecoderResult result;
232                     try
233                     {
234                         result = decoder.decodable( session, in );
235                     }
236                     finally
237                     {
238                         in.position( pos );
239                         in.limit( limit );
240                     }
241                     
242                     if( result == MessageDecoder.OK )
243                     {
244                         currentDecoder = decoder;
245                         break;
246                     }
247                     else if( result == MessageDecoder.NOT_OK )
248                     {
249                         undecodables ++;
250                     }
251                     else if( result != MessageDecoder.NEED_DATA )
252                     {
253                         throw new IllegalStateException( "Unexpected decode result (see your decodable()): " + result );
254                     }
255                 }
256                 
257                 if( undecodables == decoders.length )
258                 {
259                     // Throw an exception if all decoders cannot decode data.
260                     in.position( in.limit() ); // Skip data
261                     throw new ProtocolViolationException(
262                             "No appropriate message decoder: " + in.getHexDump() );
263                 }
264                 
265                 if( currentDecoder == null )
266                 {
267                     // Decoder is not determined yet (i.e. we need more data)
268                     return false;
269                 }
270             }
271             
272             MessageDecoderResult result = currentDecoder.decode( session, in, out );
273             if( result == MessageDecoder.OK )
274             {
275                 currentDecoder = null;
276                 return true;
277             }
278             else if( result == MessageDecoder.NEED_DATA )
279             {
280                 return false;
281             }
282             else if( result == MessageDecoder.NOT_OK )
283             {
284                 throw new ProtocolViolationException( "Message decoder returned NOT_OK." );
285             }
286             else
287             {
288                 throw new IllegalStateException( "Unexpected decode result (see your decode()): " + result );
289             }
290         }
291     }
292     
293     private static class SingletonMessageEncoderFactory implements MessageEncoderFactory
294     {
295         private final MessageEncoder encoder;
296         
297         private SingletonMessageEncoderFactory( MessageEncoder encoder )
298         {
299             if( encoder == null )
300             {
301                 throw new NullPointerException( "encoder" );
302             }
303             this.encoder = encoder;
304         }
305 
306         public MessageEncoder newEncoder()
307         {
308             return encoder;
309         }
310     }
311 
312     private static class SingletonMessageDecoderFactory implements MessageDecoderFactory
313     {
314         private final MessageDecoder decoder;
315         
316         private SingletonMessageDecoderFactory( MessageDecoder decoder )
317         {
318             if( decoder == null )
319             {
320                 throw new NullPointerException( "decoder" );
321             }
322             this.decoder = decoder;
323         }
324 
325         public MessageDecoder newDecoder()
326         {
327             return decoder;
328         }
329     }
330     
331     private static class DefaultConstructorMessageEncoderFactory implements MessageEncoderFactory
332     {
333         private final Class encoderClass;
334         
335         private DefaultConstructorMessageEncoderFactory( Class encoderClass )
336         {
337             if( encoderClass == null )
338             {
339                 throw new NullPointerException( "encoderClass" );
340             }
341             
342             if( !MessageEncoder.class.isAssignableFrom( encoderClass ) )
343             {
344                 throw new IllegalArgumentException( "encoderClass is not assignable to MessageEncoder" );
345             }
346             this.encoderClass = encoderClass;
347         }
348 
349         public MessageEncoder newEncoder()
350         {
351             try
352             {
353                 return ( MessageEncoder ) encoderClass.newInstance();
354             }
355             catch( Exception e )
356             {
357                 throw new RuntimeException( "Failed to create a new instance of " + encoderClass, e );
358             }
359         }
360     }
361 
362     private static class DefaultConstructorMessageDecoderFactory implements MessageDecoderFactory
363     {
364         private final Class decoderClass;
365         
366         private DefaultConstructorMessageDecoderFactory( Class decoderClass )
367         {
368             if( decoderClass == null )
369             {
370                 throw new NullPointerException( "decoderClass" );
371             }
372             
373             if( !MessageDecoder.class.isAssignableFrom( decoderClass ) )
374             {
375                 throw new IllegalArgumentException( "decoderClass is not assignable to MessageDecoder" );
376             }
377             this.decoderClass = decoderClass;
378         }
379 
380         public MessageDecoder newDecoder()
381         {
382             try
383             {
384                 return ( MessageDecoder ) decoderClass.newInstance();
385             }
386             catch( Exception e )
387             {
388                 throw new RuntimeException( "Failed to create a new instance of " + decoderClass, e );
389             }
390         }
391     }
392 }