View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  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,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.mina.filter.codec.demux;
21  
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.common.IoSession;
29  import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
30  import org.apache.mina.filter.codec.ProtocolCodecFactory;
31  import org.apache.mina.filter.codec.ProtocolDecoder;
32  import org.apache.mina.filter.codec.ProtocolDecoderException;
33  import org.apache.mina.filter.codec.ProtocolDecoderOutput;
34  import org.apache.mina.filter.codec.ProtocolEncoder;
35  import org.apache.mina.filter.codec.ProtocolEncoderException;
36  import org.apache.mina.filter.codec.ProtocolEncoderOutput;
37  import org.apache.mina.util.IdentityHashSet;
38  
39  /**
40   * A composite {@link ProtocolCodecFactory} that consists of multiple
41   * {@link MessageEncoder}s and {@link MessageDecoder}s.
42   * {@link ProtocolEncoder} and {@link ProtocolDecoder} this factory
43   * returns demultiplex incoming messages and buffers to
44   * appropriate {@link MessageEncoder}s and {@link MessageDecoder}s. 
45   * 
46   * <h2>Disposing resources acquired by {@link MessageEncoder} and {@link MessageDecoder}</h2>
47   * <p>
48   * Make your {@link MessageEncoder} and {@link MessageDecoder} to put all
49   * resources that need to be released as a session attribute.  {@link #disposeCodecResources(IoSession)}
50   * method will be invoked when a session is closed.  Override {@link #disposeCodecResources(IoSession)}
51   * to release the resources you've put as an attribute.
52   * <p>
53   * We didn't provide any <tt>dispose</tt> method for {@link MessageEncoder} and {@link MessageDecoder}
54   * because they can give you a big performance penalty in case you have a lot of
55   * message types to handle.
56   * 
57   * @author The Apache Directory Project (mina-dev@directory.apache.org)
58   * @version $Rev: 575234 $, $Date: 2007-09-13 18:23:37 +0900 (목, 13  9월 2007) $
59   * 
60   * @see MessageEncoder
61   * @see MessageDecoder
62   */
63  public class DemuxingProtocolCodecFactory implements ProtocolCodecFactory {
64      private MessageDecoderFactory[] decoderFactories = new MessageDecoderFactory[0];
65  
66      private MessageEncoderFactory[] encoderFactories = new MessageEncoderFactory[0];
67  
68      private static final Class<?>[] EMPTY_PARAMS = new Class[0];
69  
70      public DemuxingProtocolCodecFactory() {
71      }
72  
73      public void register(Class<?> encoderOrDecoderClass) {
74          if (encoderOrDecoderClass == null) {
75              throw new NullPointerException("encoderOrDecoderClass");
76          }
77  
78          try {
79              encoderOrDecoderClass.getConstructor(EMPTY_PARAMS);
80          } catch (NoSuchMethodException e) {
81              throw new IllegalArgumentException(
82                      "The specifiec class doesn't have a public default constructor.");
83          }
84  
85          boolean registered = false;
86          if (MessageEncoder.class.isAssignableFrom(encoderOrDecoderClass)) {
87              register(new DefaultConstructorMessageEncoderFactory(
88                      encoderOrDecoderClass));
89              registered = true;
90          }
91  
92          if (MessageDecoder.class.isAssignableFrom(encoderOrDecoderClass)) {
93              register(new DefaultConstructorMessageDecoderFactory(
94                      encoderOrDecoderClass));
95              registered = true;
96          }
97  
98          if (!registered) {
99              throw new IllegalArgumentException("Unregisterable type: "
100                     + encoderOrDecoderClass);
101         }
102     }
103 
104     public void register(MessageEncoder encoder) {
105         register(new SingletonMessageEncoderFactory(encoder));
106     }
107 
108     public void register(MessageEncoderFactory factory) {
109         if (factory == null) {
110             throw new NullPointerException("factory");
111         }
112         MessageEncoderFactory[] encoderFactories = this.encoderFactories;
113         MessageEncoderFactory[] newEncoderFactories = new MessageEncoderFactory[encoderFactories.length + 1];
114         System.arraycopy(encoderFactories, 0, newEncoderFactories, 0,
115                 encoderFactories.length);
116         newEncoderFactories[encoderFactories.length] = factory;
117         this.encoderFactories = newEncoderFactories;
118     }
119 
120     public void register(MessageDecoder decoder) {
121         register(new SingletonMessageDecoderFactory(decoder));
122     }
123 
124     public void register(MessageDecoderFactory factory) {
125         if (factory == null) {
126             throw new NullPointerException("factory");
127         }
128         MessageDecoderFactory[] decoderFactories = this.decoderFactories;
129         MessageDecoderFactory[] newDecoderFactories = new MessageDecoderFactory[decoderFactories.length + 1];
130         System.arraycopy(decoderFactories, 0, newDecoderFactories, 0,
131                 decoderFactories.length);
132         newDecoderFactories[decoderFactories.length] = factory;
133         this.decoderFactories = newDecoderFactories;
134     }
135 
136     public ProtocolEncoder getEncoder() throws Exception {
137         return new ProtocolEncoderImpl();
138     }
139 
140     public ProtocolDecoder getDecoder() throws Exception {
141         return new ProtocolDecoderImpl();
142     }
143 
144     /**
145      * Implement this method to release all resources acquired to perform
146      * encoding and decoding messages for the specified <tt>session</tt>.
147      * By default, this method does nothing.
148      * 
149      * @param session the session that requires resource deallocation now
150      */
151     protected void disposeCodecResources(IoSession session) {
152         // Do nothing by default; let users implement it as they want.
153 
154         // This statement is just to avoid compiler warning.  Please ignore. 
155         session.getTransportType();
156     }
157 
158     private class ProtocolEncoderImpl implements ProtocolEncoder {
159         private final Map<Class<?>, MessageEncoder> encoders = new IdentityHashMap<Class<?>, MessageEncoder>();
160 
161         private ProtocolEncoderImpl() throws Exception {
162             MessageEncoderFactory[] encoderFactories = DemuxingProtocolCodecFactory.this.encoderFactories;
163             for (int i = encoderFactories.length - 1; i >= 0; i--) {
164                 MessageEncoder encoder = encoderFactories[i].getEncoder();
165                 Set<Class<?>> messageTypes = encoder.getMessageTypes();
166                 if (messageTypes == null) {
167                     throw new IllegalStateException(encoder.getClass()
168                             .getName()
169                             + "#getMessageTypes() may not return null.");
170                 }
171 
172                 Iterator<Class<?>> it = messageTypes.iterator();
173                 while (it.hasNext()) {
174                     Class<?> type = it.next();
175                     encoders.put(type, encoder);
176                 }
177             }
178         }
179 
180         public void encode(IoSession session, Object message,
181                 ProtocolEncoderOutput out) throws Exception {
182             Class<?> type = message.getClass();
183             MessageEncoder encoder = findEncoder(type);
184             if (encoder == null) {
185                 throw new ProtocolEncoderException("Unexpected message type: "
186                         + type);
187             }
188 
189             encoder.encode(session, message, out);
190         }
191 
192         private MessageEncoder findEncoder(Class<?> type) {
193             MessageEncoder encoder = encoders.get(type);
194             if (encoder == null) {
195                 encoder = findEncoder(type, new IdentityHashSet<Class<?>>());
196             }
197 
198             return encoder;
199         }
200 
201         private MessageEncoder findEncoder(Class<?> type,
202                 Set<Class<?>> triedClasses) {
203             MessageEncoder encoder;
204 
205             if (triedClasses.contains(type))
206                 return null;
207             triedClasses.add(type);
208 
209             encoder = encoders.get(type);
210             if (encoder == null) {
211                 encoder = findEncoder(type, triedClasses);
212                 if (encoder != null)
213                     return encoder;
214 
215                 Class<?>[] interfaces = type.getInterfaces();
216                 for (int i = 0; i < interfaces.length; i++) {
217                     encoder = findEncoder(interfaces[i], triedClasses);
218                     if (encoder != null)
219                         return encoder;
220                 }
221 
222                 return null;
223             } else
224                 return encoder;
225         }
226 
227         public void dispose(IoSession session) throws Exception {
228             DemuxingProtocolCodecFactory.this.disposeCodecResources(session);
229         }
230     }
231 
232     private class ProtocolDecoderImpl extends CumulativeProtocolDecoder {
233         private final MessageDecoder[] decoders;
234 
235         private MessageDecoder currentDecoder;
236 
237         protected ProtocolDecoderImpl() throws Exception {
238             MessageDecoderFactory[] decoderFactories = DemuxingProtocolCodecFactory.this.decoderFactories;
239             decoders = new MessageDecoder[decoderFactories.length];
240             for (int i = decoderFactories.length - 1; i >= 0; i--) {
241                 decoders[i] = decoderFactories[i].getDecoder();
242             }
243         }
244 
245         protected boolean doDecode(IoSession session, ByteBuffer in,
246                 ProtocolDecoderOutput out) throws Exception {
247             if (currentDecoder == null) {
248                 MessageDecoder[] decoders = this.decoders;
249                 int undecodables = 0;
250                 for (int i = decoders.length - 1; i >= 0; i--) {
251                     MessageDecoder decoder = decoders[i];
252                     int limit = in.limit();
253                     int pos = in.position();
254 
255                     MessageDecoderResult result;
256                     try {
257                         result = decoder.decodable(session, in);
258                     } finally {
259                         in.position(pos);
260                         in.limit(limit);
261                     }
262 
263                     if (result == MessageDecoder.OK) {
264                         currentDecoder = decoder;
265                         break;
266                     } else if (result == MessageDecoder.NOT_OK) {
267                         undecodables++;
268                     } else if (result != MessageDecoder.NEED_DATA) {
269                         throw new IllegalStateException(
270                                 "Unexpected decode result (see your decodable()): "
271                                         + result);
272                     }
273                 }
274 
275                 if (undecodables == decoders.length) {
276                     // Throw an exception if all decoders cannot decode data.
277                     String dump = in.getHexDump();
278                     in.position(in.limit()); // Skip data
279                     throw new ProtocolDecoderException(
280                             "No appropriate message decoder: " + dump);
281                 }
282 
283                 if (currentDecoder == null) {
284                     // Decoder is not determined yet (i.e. we need more data)
285                     return false;
286                 }
287             }
288 
289             MessageDecoderResult result = currentDecoder.decode(session, in,
290                     out);
291             if (result == MessageDecoder.OK) {
292                 currentDecoder = null;
293                 return true;
294             } else if (result == MessageDecoder.NEED_DATA) {
295                 return false;
296             } else if (result == MessageDecoder.NOT_OK) {
297                 currentDecoder = null;
298                 throw new ProtocolDecoderException(
299                         "Message decoder returned NOT_OK.");
300             } else {
301                 currentDecoder = null;
302                 throw new IllegalStateException(
303                         "Unexpected decode result (see your decode()): "
304                                 + result);
305             }
306         }
307 
308         public void finishDecode(IoSession session, ProtocolDecoderOutput out)
309                 throws Exception {
310             if (currentDecoder == null) {
311                 return;
312             }
313 
314             currentDecoder.finishDecode(session, out);
315         }
316 
317         public void dispose(IoSession session) throws Exception {
318             super.dispose(session);
319 
320             // ProtocolEncoder.dispose() already called disposeCodec(),
321             // so there's nothing more we need to do.
322         }
323     }
324 
325     private static class SingletonMessageEncoderFactory implements
326             MessageEncoderFactory {
327         private final MessageEncoder encoder;
328 
329         private SingletonMessageEncoderFactory(MessageEncoder encoder) {
330             if (encoder == null) {
331                 throw new NullPointerException("encoder");
332             }
333             this.encoder = encoder;
334         }
335 
336         public MessageEncoder getEncoder() {
337             return encoder;
338         }
339     }
340 
341     private static class SingletonMessageDecoderFactory implements
342             MessageDecoderFactory {
343         private final MessageDecoder decoder;
344 
345         private SingletonMessageDecoderFactory(MessageDecoder decoder) {
346             if (decoder == null) {
347                 throw new NullPointerException("decoder");
348             }
349             this.decoder = decoder;
350         }
351 
352         public MessageDecoder getDecoder() {
353             return decoder;
354         }
355     }
356 
357     private static class DefaultConstructorMessageEncoderFactory implements
358             MessageEncoderFactory {
359         private final Class<?> encoderClass;
360 
361         private DefaultConstructorMessageEncoderFactory(Class<?> encoderClass) {
362             if (encoderClass == null) {
363                 throw new NullPointerException("encoderClass");
364             }
365 
366             if (!MessageEncoder.class.isAssignableFrom(encoderClass)) {
367                 throw new IllegalArgumentException(
368                         "encoderClass is not assignable to MessageEncoder");
369             }
370             this.encoderClass = encoderClass;
371         }
372 
373         public MessageEncoder getEncoder() throws Exception {
374             return (MessageEncoder) encoderClass.newInstance();
375         }
376     }
377 
378     private static class DefaultConstructorMessageDecoderFactory implements
379             MessageDecoderFactory {
380         private final Class<?> decoderClass;
381 
382         private DefaultConstructorMessageDecoderFactory(Class<?> decoderClass) {
383             if (decoderClass == null) {
384                 throw new NullPointerException("decoderClass");
385             }
386 
387             if (!MessageDecoder.class.isAssignableFrom(decoderClass)) {
388                 throw new IllegalArgumentException(
389                         "decoderClass is not assignable to MessageDecoder");
390             }
391             this.decoderClass = decoderClass;
392         }
393 
394         public MessageDecoder getDecoder() throws Exception {
395             return (MessageDecoder) decoderClass.newInstance();
396         }
397     }
398 }