KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > mina > filter > codec > demux > DemuxingProtocolCodecFactory


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 JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Set JavaDoc;
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: 555855 $, $Date: 2007-07-13 12:19:00 +0900 (금, 13 7월 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 JavaDoc<?>[] EMPTY_PARAMS = new Class JavaDoc[0];
69
70     public DemuxingProtocolCodecFactory() {
71     }
72
73     public void register(Class JavaDoc<?> encoderOrDecoderClass) {
74         if (encoderOrDecoderClass == null) {
75             throw new NullPointerException JavaDoc("encoderOrDecoderClass");
76         }
77
78         try {
79             encoderOrDecoderClass.getConstructor(EMPTY_PARAMS);
80         } catch (NoSuchMethodException JavaDoc e) {
81             throw new IllegalArgumentException JavaDoc(
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 JavaDoc("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 JavaDoc("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 JavaDoc("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 JavaDoc {
137         return new ProtocolEncoderImpl();
138     }
139
140     public ProtocolDecoder getDecoder() throws Exception JavaDoc {
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 JavaDoc<Class JavaDoc<?>, MessageEncoder> encoders = new IdentityHashMap JavaDoc<Class JavaDoc<?>, MessageEncoder>();
160
161         private ProtocolEncoderImpl() throws Exception JavaDoc {
162             MessageEncoderFactory[] encoderFactories = DemuxingProtocolCodecFactory.this.encoderFactories;
163             for (int i = encoderFactories.length - 1; i >= 0; i--) {
164                 MessageEncoder encoder = encoderFactories[i].getEncoder();
165                 Set JavaDoc<Class JavaDoc<?>> messageTypes = encoder.getMessageTypes();
166                 if (messageTypes == null) {
167                     throw new IllegalStateException JavaDoc(encoder.getClass()
168                             .getName()
169                             + "#getMessageTypes() may not return null.");
170                 }
171
172                 Iterator JavaDoc<Class JavaDoc<?>> it = messageTypes.iterator();
173                 while (it.hasNext()) {
174                     Class JavaDoc<?> type = it.next();
175                     encoders.put(type, encoder);
176                 }
177             }
178         }
179
180         public void encode(IoSession session, Object JavaDoc message,
181                 ProtocolEncoderOutput out) throws Exception JavaDoc {
182             Class JavaDoc<?> 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 JavaDoc<?> type) {
193             MessageEncoder encoder = encoders.get(type);
194             if (encoder == null) {
195                 encoder = findEncoder(type, new IdentityHashSet<Class JavaDoc<?>>());
196             }
197
198             return encoder;
199         }
200
201         private MessageEncoder findEncoder(Class JavaDoc<?> type,
202                 Set JavaDoc<Class JavaDoc<?>> 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 JavaDoc<?>[] 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 JavaDoc {
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 JavaDoc {
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 JavaDoc {
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 JavaDoc(
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 JavaDoc 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                 throw new ProtocolDecoderException(
298                         "Message decoder returned NOT_OK.");
299             } else {
300                 throw new IllegalStateException JavaDoc(
301                         "Unexpected decode result (see your decode()): "
302                                 + result);
303             }
304         }
305
306         public void finishDecode(IoSession session, ProtocolDecoderOutput out)
307                 throws Exception JavaDoc {
308             if (currentDecoder == null) {
309                 return;
310             }
311
312             currentDecoder.finishDecode(session, out);
313         }
314
315         public void dispose(IoSession session) throws Exception JavaDoc {
316             super.dispose(session);
317
318             // ProtocolEncoder.dispose() already called disposeCodec(),
319
// so there's nothing more we need to do.
320
}
321     }
322
323     private static class SingletonMessageEncoderFactory implements
324             MessageEncoderFactory {
325         private final MessageEncoder encoder;
326
327         private SingletonMessageEncoderFactory(MessageEncoder encoder) {
328             if (encoder == null) {
329                 throw new NullPointerException JavaDoc("encoder");
330             }
331             this.encoder = encoder;
332         }
333
334         public MessageEncoder getEncoder() {
335             return encoder;
336         }
337     }
338
339     private static class SingletonMessageDecoderFactory implements
340             MessageDecoderFactory {
341         private final MessageDecoder decoder;
342
343         private SingletonMessageDecoderFactory(MessageDecoder decoder) {
344             if (decoder == null) {
345                 throw new NullPointerException JavaDoc("decoder");
346             }
347             this.decoder = decoder;
348         }
349
350         public MessageDecoder getDecoder() {
351             return decoder;
352         }
353     }
354
355     private static class DefaultConstructorMessageEncoderFactory implements
356             MessageEncoderFactory {
357         private final Class JavaDoc<?> encoderClass;
358
359         private DefaultConstructorMessageEncoderFactory(Class JavaDoc<?> encoderClass) {
360             if (encoderClass == null) {
361                 throw new NullPointerException JavaDoc("encoderClass");
362             }
363
364             if (!MessageEncoder.class.isAssignableFrom(encoderClass)) {
365                 throw new IllegalArgumentException JavaDoc(
366                         "encoderClass is not assignable to MessageEncoder");
367             }
368             this.encoderClass = encoderClass;
369         }
370
371         public MessageEncoder getEncoder() throws Exception JavaDoc {
372             return (MessageEncoder) encoderClass.newInstance();
373         }
374     }
375
376     private static class DefaultConstructorMessageDecoderFactory implements
377             MessageDecoderFactory {
378         private final Class JavaDoc<?> decoderClass;
379
380         private DefaultConstructorMessageDecoderFactory(Class JavaDoc<?> decoderClass) {
381             if (decoderClass == null) {
382                 throw new NullPointerException JavaDoc("decoderClass");
383             }
384
385             if (!MessageDecoder.class.isAssignableFrom(decoderClass)) {
386                 throw new IllegalArgumentException JavaDoc(
387                         "decoderClass is not assignable to MessageDecoder");
388             }
389             this.decoderClass = decoderClass;
390         }
391
392         public MessageDecoder getDecoder() throws Exception JavaDoc {
393             return (MessageDecoder) decoderClass.newInstance();
394         }
395     }
396 }
397
Popular Tags