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 java.nio.ByteBuffer;
23  
24  import org.apache.mina.api.AbstractIoFilter;
25  import org.apache.mina.api.IoFilter;
26  import org.apache.mina.api.IoSession;
27  import org.apache.mina.codec.ProtocolDecoder;
28  import org.apache.mina.codec.ProtocolDecoderException;
29  import org.apache.mina.codec.ProtocolEncoder;
30  import org.apache.mina.filterchain.ReadFilterChainController;
31  import org.apache.mina.filterchain.WriteFilterChainController;
32  import org.apache.mina.session.AttributeKey;
33  import org.apache.mina.session.WriteRequest;
34  import org.apache.mina.util.Assert;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   * An {@link IoFilter} which translates binary or protocol specific data into message objects and vice versa using
40   * {@link ProtocolCodecFactory}, {@link ProtocolEncoder}, or {@link ProtocolDecoder}.
41   * 
42   * @param MESSAGE the kind of high level business message this filter will encode and decode.
43   * @param ENCODED the kind of low level message (most of time {@link ByteBuffer}) this filter will produce of consume.
44   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
45   */
46  public class ProtocolCodecFilter<MESSAGE, ENCODED, ENCODING_STATE, DECODING_STATE> extends AbstractIoFilter {
47      /** A logger for this class */
48      private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolCodecFilter.class);
49  
50      /** the immutable encoder */
51      private final ProtocolEncoder<MESSAGE, ENCODED, ENCODING_STATE> encoder;
52  
53      /** the immutable decoder */
54      private final ProtocolDecoder<ENCODED, MESSAGE, DECODING_STATE> decoder;
55  
56      /** key for session attribute holding the encoder */
57      private static final AttributeKey<Object> ENCODER = new AttributeKey<Object>(Object.class, "internal_encoder");
58  
59      /** key for session attribute holding the decoder */
60      private static final AttributeKey<Object> DECODER = new AttributeKey<Object>(Object.class, "internal_decoder");
61  
62      /**
63       * Creates a new instance of ProtocolCodecFilter, with the specified encoder and decoder.
64       * 
65       */
66      public ProtocolCodecFilter(ProtocolEncoder<MESSAGE, ENCODED, ENCODING_STATE> encoder,
67              ProtocolDecoder<ENCODED, MESSAGE, DECODING_STATE> decoder) {
68          Assert.assertNotNull(encoder, "encoder");
69          Assert.assertNotNull(decoder, "decoder");
70          this.encoder = encoder;
71          this.decoder = decoder;
72      }
73  
74      /**
75       * Process the incoming message, calling the session decoder. As the incoming buffer might contains more than one
76       * messages, we have to loop until the decoder throws an exception. <code>
77       *  while ( buffer not empty )
78       *    try
79       *      decode ( buffer )
80       *    catch
81       *      break;
82       * </code>
83       */
84      @SuppressWarnings("unchecked")
85      @Override
86      public void messageReceived(IoSession session, Object in, ReadFilterChainController controller) {
87          LOGGER.debug("Processing a MESSAGE_RECEIVED for session {}", session);
88  
89          DECODING_STATE state = getDecodingState(session);
90  
91          // Loop until the decoder cannot decode more
92          MESSAGE msg;
93          try {
94              while (((msg = decoder.decode((ENCODED) in, state)) != null)) {
95                  controller.callReadNextFilter(msg);
96              }
97          } catch (ProtocolDecoderException e) {
98              LOGGER.debug("decoding exception : ", e);
99              throw e;
100         }
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     @SuppressWarnings("unchecked")
107     @Override
108     public void messageWriting(IoSession session, WriteRequest message, WriteFilterChainController controller) {
109         LOGGER.debug("Processing a MESSAGE_WRITTING for session {}", session);
110 
111         ENCODED encoded = encoder.encode((MESSAGE) message.getMessage(), getEncodingState(session));
112         message.setMessage(encoded);
113 
114         controller.callWriteNextFilter(message);
115     }
116 
117     /**
118      * {@inheritDoc}
119      */
120     @Override
121     public void sessionOpened(IoSession session) {
122         // Initialize the encoder and decoder state
123 
124         ENCODING_STATE encodingState = encoder.createEncoderState();
125         session.setAttribute(ENCODER, encodingState);
126 
127         DECODING_STATE decodingState = decoder.createDecoderState();
128         session.setAttribute(DECODER, decodingState);
129     }
130 
131     /**
132      * {@inheritDoc}
133      */
134     @Override
135     public void sessionClosed(IoSession session) {
136         decoder.finishDecode(getDecodingState(session));
137 
138     }
139 
140     // ----------- Helper methods ---------------------------------------------
141 
142     @SuppressWarnings("unchecked")
143     private DECODING_STATE getDecodingState(IoSession session) {
144         return (DECODING_STATE) session.getAttribute(DECODER);
145     }
146 
147     @SuppressWarnings("unchecked")
148     private ENCODING_STATE getEncodingState(IoSession session) {
149         return (ENCODING_STATE) session.getAttribute(ENCODER);
150     }
151 
152 }