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;
21  
22  import org.apache.mina.common.ByteBuffer;
23  import org.apache.mina.common.ByteBufferProxy;
24  import org.apache.mina.common.IoFilter;
25  import org.apache.mina.common.IoFilterAdapter;
26  import org.apache.mina.common.IoFilterChain;
27  import org.apache.mina.common.IoSession;
28  import org.apache.mina.common.WriteFuture;
29  import org.apache.mina.common.support.DefaultWriteFuture;
30  import org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput;
31  import org.apache.mina.filter.codec.support.SimpleProtocolEncoderOutput;
32  import org.apache.mina.util.SessionLog;
33  
34  /**
35   * An {@link IoFilter} which translates binary or protocol specific data into
36   * message object and vice versa using {@link ProtocolCodecFactory},
37   * {@link ProtocolEncoder}, or {@link ProtocolDecoder}.
38   * 
39   * @author The Apache Directory Project (mina-dev@directory.apache.org)
40   * @version $Rev: 560324 $, $Date: 2007-07-28 02:21:08 +0900 (토, 28  7월 2007) $
41   */
42  public class ProtocolCodecFilter extends IoFilterAdapter {
43      public static final String ENCODER = ProtocolCodecFilter.class.getName()
44              + ".encoder";
45  
46      public static final String DECODER = ProtocolCodecFilter.class.getName()
47              + ".decoder";
48      
49      private static final String DECODER_OUT = ProtocolCodecFilter.class.getName()
50              + ".decoderOut";
51  
52      private static final Class[] EMPTY_PARAMS = new Class[0];
53  
54      private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]);
55  
56      private final ProtocolCodecFactory factory;
57  
58      public ProtocolCodecFilter(ProtocolCodecFactory factory) {
59          if (factory == null) {
60              throw new NullPointerException("factory");
61          }
62          this.factory = factory;
63      }
64  
65      public ProtocolCodecFilter(final ProtocolEncoder encoder,
66              final ProtocolDecoder decoder) {
67          if (encoder == null) {
68              throw new NullPointerException("encoder");
69          }
70          if (decoder == null) {
71              throw new NullPointerException("decoder");
72          }
73  
74          this.factory = new ProtocolCodecFactory() {
75              public ProtocolEncoder getEncoder() {
76                  return encoder;
77              }
78  
79              public ProtocolDecoder getDecoder() {
80                  return decoder;
81              }
82          };
83      }
84  
85      public ProtocolCodecFilter(final Class encoderClass,
86              final Class decoderClass) {
87          if (encoderClass == null) {
88              throw new NullPointerException("encoderClass");
89          }
90          if (decoderClass == null) {
91              throw new NullPointerException("decoderClass");
92          }
93          if (!ProtocolEncoder.class.isAssignableFrom(encoderClass)) {
94              throw new IllegalArgumentException("encoderClass: "
95                      + encoderClass.getName());
96          }
97          if (!ProtocolDecoder.class.isAssignableFrom(decoderClass)) {
98              throw new IllegalArgumentException("decoderClass: "
99                      + decoderClass.getName());
100         }
101         try {
102             encoderClass.getConstructor(EMPTY_PARAMS);
103         } catch (NoSuchMethodException e) {
104             throw new IllegalArgumentException(
105                     "encoderClass doesn't have a public default constructor.");
106         }
107         try {
108             decoderClass.getConstructor(EMPTY_PARAMS);
109         } catch (NoSuchMethodException e) {
110             throw new IllegalArgumentException(
111                     "decoderClass doesn't have a public default constructor.");
112         }
113 
114         this.factory = new ProtocolCodecFactory() {
115             public ProtocolEncoder getEncoder() throws Exception {
116                 return (ProtocolEncoder) encoderClass.newInstance();
117             }
118 
119             public ProtocolDecoder getDecoder() throws Exception {
120                 return (ProtocolDecoder) decoderClass.newInstance();
121             }
122         };
123     }
124 
125     public void onPreAdd(IoFilterChain parent, String name,
126             NextFilter nextFilter) throws Exception {
127         if (parent.contains(ProtocolCodecFilter.class)) {
128             throw new IllegalStateException(
129                     "A filter chain cannot contain more than one ProtocolCodecFilter.");
130         }
131     }
132 
133     public void onPostRemove(IoFilterChain parent, String name,
134             NextFilter nextFilter) throws Exception {
135         disposeEncoder(parent.getSession());
136         disposeDecoder(parent.getSession());
137         disposeDecoderOut(parent.getSession());
138     }
139 
140     public void messageReceived(NextFilter nextFilter, IoSession session,
141             Object message) throws Exception {
142         if (!(message instanceof ByteBuffer)) {
143             nextFilter.messageReceived(session, message);
144             return;
145         }
146 
147         ByteBuffer in = (ByteBuffer) message;
148         ProtocolDecoder decoder = getDecoder(session);
149         ProtocolDecoderOutput decoderOut = getDecoderOut(session, nextFilter);
150 
151         try {
152             synchronized (decoderOut) {
153                 decoder.decode(session, in, decoderOut);
154             }
155         } catch (Throwable t) {
156             ProtocolDecoderException pde;
157             if (t instanceof ProtocolDecoderException) {
158                 pde = (ProtocolDecoderException) t;
159             } else {
160                 pde = new ProtocolDecoderException(t);
161             }
162             pde.setHexdump(in.getHexDump());
163             throw pde;
164         } finally {
165             // Release the read buffer.
166             in.release();
167 
168             decoderOut.flush();
169         }
170     }
171 
172     public void messageSent(NextFilter nextFilter, IoSession session,
173             Object message) throws Exception {
174         if (message instanceof HiddenByteBuffer) {
175             return;
176         }
177 
178         if (!(message instanceof MessageByteBuffer)) {
179             nextFilter.messageSent(session, message);
180             return;
181         }
182 
183         nextFilter.messageSent(session, ((MessageByteBuffer) message).message);
184     }
185 
186     public void filterWrite(NextFilter nextFilter, IoSession session,
187             WriteRequest writeRequest) throws Exception {
188         Object message = writeRequest.getMessage();
189         if (message instanceof ByteBuffer) {
190             nextFilter.filterWrite(session, writeRequest);
191             return;
192         }
193 
194         ProtocolEncoder encoder = getEncoder(session);
195         ProtocolEncoderOutputImpl encoderOut = getEncoderOut(session,
196                 nextFilter, writeRequest);
197 
198         try {
199             encoder.encode(session, message, encoderOut);
200             encoderOut.flush();
201             nextFilter.filterWrite(session, new WriteRequest(
202                     new MessageByteBuffer(writeRequest.getMessage()),
203                     writeRequest.getFuture(), writeRequest.getDestination()));
204         } catch (Throwable t) {
205             ProtocolEncoderException pee;
206             if (t instanceof ProtocolEncoderException) {
207                 pee = (ProtocolEncoderException) t;
208             } else {
209                 pee = new ProtocolEncoderException(t);
210             }
211             throw pee;
212         } finally {
213             // Dispose the encoder if this session is connectionless.
214             if (session.getTransportType().isConnectionless()) {
215                 disposeEncoder(session);
216             }
217         }
218     }
219 
220     public void sessionClosed(NextFilter nextFilter, IoSession session)
221             throws Exception {
222         // Call finishDecode() first when a connection is closed.
223         ProtocolDecoder decoder = getDecoder(session);
224         ProtocolDecoderOutput decoderOut = getDecoderOut(session, nextFilter);
225         try {
226             decoder.finishDecode(session, decoderOut);
227         } catch (Throwable t) {
228             ProtocolDecoderException pde;
229             if (t instanceof ProtocolDecoderException) {
230                 pde = (ProtocolDecoderException) t;
231             } else {
232                 pde = new ProtocolDecoderException(t);
233             }
234             throw pde;
235         } finally {
236             // Dispose all.
237             disposeEncoder(session);
238             disposeDecoder(session);
239             disposeDecoderOut(session);
240             decoderOut.flush();
241         }
242 
243         nextFilter.sessionClosed(session);
244     }
245 
246     private ProtocolEncoder getEncoder(IoSession session) throws Exception {
247         ProtocolEncoder encoder = (ProtocolEncoder) session
248                 .getAttribute(ENCODER);
249         if (encoder == null) {
250             encoder = factory.getEncoder();
251             session.setAttribute(ENCODER, encoder);
252         }
253         return encoder;
254     }
255 
256     private ProtocolEncoderOutputImpl getEncoderOut(IoSession session,
257             NextFilter nextFilter, WriteRequest writeRequest) {
258         return new ProtocolEncoderOutputImpl(session, nextFilter, writeRequest);
259     }
260 
261     private ProtocolDecoder getDecoder(IoSession session) throws Exception {
262         ProtocolDecoder decoder = (ProtocolDecoder) session
263                 .getAttribute(DECODER);
264         if (decoder == null) {
265             decoder = factory.getDecoder();
266             session.setAttribute(DECODER, decoder);
267         }
268         return decoder;
269     }
270 
271     private ProtocolDecoderOutput getDecoderOut(IoSession session,
272             NextFilter nextFilter) {
273         ProtocolDecoderOutput out = (ProtocolDecoderOutput) session.getAttribute(DECODER_OUT);
274         if (out == null) {
275             out = new SimpleProtocolDecoderOutput(session, nextFilter);
276             session.setAttribute(DECODER_OUT, out);
277         }
278 
279         return out;
280     }
281 
282     private void disposeEncoder(IoSession session) {
283         ProtocolEncoder encoder = (ProtocolEncoder) session
284                 .removeAttribute(ENCODER);
285         if (encoder == null) {
286             return;
287         }
288 
289         try {
290             encoder.dispose(session);
291         } catch (Throwable t) {
292             SessionLog.warn(session, "Failed to dispose: "
293                     + encoder.getClass().getName() + " (" + encoder + ')');
294         }
295     }
296 
297     private void disposeDecoder(IoSession session) {
298         ProtocolDecoder decoder = (ProtocolDecoder) session
299                 .removeAttribute(DECODER);
300         if (decoder == null) {
301             return;
302         }
303 
304         try {
305             decoder.dispose(session);
306         } catch (Throwable t) {
307             SessionLog.warn(session, "Falied to dispose: "
308                     + decoder.getClass().getName() + " (" + decoder + ')');
309         }
310     }
311     
312     private void disposeDecoderOut(IoSession session) {
313         session.removeAttribute(DECODER_OUT);
314     }
315     
316     private static class HiddenByteBuffer extends ByteBufferProxy {
317         private HiddenByteBuffer(ByteBuffer buf) {
318             super(buf);
319         }
320     }
321 
322     private static class MessageByteBuffer extends ByteBufferProxy {
323         private final Object message;
324 
325         private MessageByteBuffer(Object message) {
326             super(EMPTY_BUFFER);
327             this.message = message;
328         }
329 
330         public void acquire() {
331             // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message
332         }
333 
334         public void release() {
335             // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message
336         }
337     }
338 
339     private static class ProtocolEncoderOutputImpl extends
340             SimpleProtocolEncoderOutput {
341         private final IoSession session;
342 
343         private final NextFilter nextFilter;
344 
345         private final WriteRequest writeRequest;
346 
347         public ProtocolEncoderOutputImpl(IoSession session,
348                 NextFilter nextFilter, WriteRequest writeRequest) {
349             this.session = session;
350             this.nextFilter = nextFilter;
351             this.writeRequest = writeRequest;
352         }
353 
354         protected WriteFuture doFlush(ByteBuffer buf) {
355             WriteFuture future = new DefaultWriteFuture(session);
356             nextFilter.filterWrite(session, new WriteRequest(
357                     new HiddenByteBuffer(buf), future, writeRequest
358                             .getDestination()));
359             return future;
360         }
361     }
362 }