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