1 | /* |
2 | * @(#) $Id: DemuxingProtocolCodecFactory.java 264677 2005-08-30 02:44:35Z 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: 264677 $, $Date: 2005-08-30 11:44:35 +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 | } |