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 | } |