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.statemachine;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.mina.common.IoBuffer;
26  import org.apache.mina.filter.codec.ProtocolDecoderOutput;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  /**
31   * 
32   * @author The Apache MINA Project (dev@mina.apache.org)
33   * @version $Rev: 602837 $, $Date: 2007-12-10 03:03:42 -0700 (Mon, 10 Dec 2007) $
34   */
35  public abstract class DecodingStateMachine implements DecodingState {
36      private final Logger log = LoggerFactory
37              .getLogger(DecodingStateMachine.class);
38  
39      private final List<Object> childProducts = new ArrayList<Object>();
40  
41      private final ProtocolDecoderOutput childOutput = new ProtocolDecoderOutput() {
42          public void flush() {
43          }
44  
45          public void write(Object message) {
46              childProducts.add(message);
47          }
48      };
49  
50      private DecodingState currentState;
51      private boolean initialized;
52  
53      protected abstract DecodingState init() throws Exception;
54  
55      protected abstract DecodingState finishDecode(List<Object> childProducts,
56              ProtocolDecoderOutput out) throws Exception;
57  
58      protected abstract void destroy() throws Exception;
59  
60      public DecodingState decode(IoBuffer in, ProtocolDecoderOutput out)
61              throws Exception {
62          DecodingState state = getCurrentState();
63  
64          final int limit = in.limit();
65          int pos = in.position();
66  
67          try {
68              for (;;) {
69                  // Wait for more data if all data is consumed.
70                  if (pos == limit) {
71                      break;
72                  }
73  
74                  DecodingState oldState = state;
75                  state = state.decode(in, childOutput);
76  
77                  // If finished, call finishDecode
78                  if (state == null) {
79                      return finishDecode(childProducts, out);
80                  }
81  
82                  int newPos = in.position();
83  
84                  // Wait for more data if nothing is consumed and state didn't change.
85                  if (newPos == pos && oldState == state) {
86                      break;
87                  }
88                  pos = newPos;
89              }
90  
91              return this;
92          } catch (Exception e) {
93              state = null;
94              throw e;
95          } finally {
96              this.currentState = state;
97  
98              // Destroy if decoding is finished or failed.
99              if (state == null) {
100                 cleanup();
101             }
102         }
103     }
104 
105     public DecodingState finishDecode(ProtocolDecoderOutput out)
106             throws Exception {
107         DecodingState nextState;
108         DecodingState state = getCurrentState();
109         try {
110             for (;;) {
111                 DecodingState oldState = state;
112                 state = state.finishDecode(childOutput);
113                 if (state == null) {
114                     // Finished
115                     break;
116                 }
117     
118                 // Exit if state didn't change.
119                 if (oldState == state) {
120                     break;
121                 }
122             }
123         } catch (Exception e) {
124             state = null;
125             log.debug(
126                     "Ignoring the exception caused by a closed session.", e);
127         } finally {
128             this.currentState = state;
129             nextState = finishDecode(childProducts, out);
130             if (state == null) {
131                 cleanup();
132             }
133         }
134         return nextState;
135     }
136 
137     private void cleanup() {
138         if (!initialized) {
139             throw new IllegalStateException();
140         }
141         
142         initialized = false;
143         childProducts.clear();
144         try {
145             destroy();
146         } catch (Exception e2) {
147             log.warn("Failed to destroy a decoding state machine.", e2);
148         }
149     }
150 
151     private DecodingState getCurrentState() throws Exception {
152         DecodingState state = this.currentState;
153         if (state == null) {
154             state = init();
155             initialized = true;
156         }
157         return state;
158     }
159 }