View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.websocket.common.io;
20  
21  import java.io.IOException;
22  import java.util.concurrent.atomic.AtomicBoolean;
23  
24  import org.eclipse.jetty.util.log.Log;
25  import org.eclipse.jetty.util.log.Logger;
26  import org.eclipse.jetty.websocket.common.ConnectionState;
27  
28  /**
29   * Simple state tracker for Input / Output and {@link ConnectionState}
30   */
31  public class IOState
32  {
33      private static final Logger LOG = Log.getLogger(IOState.class);
34      private ConnectionState state;
35      private final AtomicBoolean inputClosed;
36      private final AtomicBoolean outputClosed;
37  
38      private final AtomicBoolean cleanClose;
39      private final AtomicBoolean remoteCloseInitiated;
40      private final AtomicBoolean localCloseInitiated;
41  
42      public IOState()
43      {
44          this.state = ConnectionState.CONNECTING;
45          this.inputClosed = new AtomicBoolean(false);
46          this.outputClosed = new AtomicBoolean(false);
47          this.remoteCloseInitiated = new AtomicBoolean(false);
48          this.localCloseInitiated = new AtomicBoolean(false);
49          this.cleanClose = new AtomicBoolean(false);
50      }
51  
52      public void assertInputOpen() throws IOException
53      {
54          if (isInputClosed())
55          {
56              throw new IOException("Connection input is closed");
57          }
58      }
59  
60      public void assertOutputOpen() throws IOException
61      {
62          if (isOutputClosed())
63          {
64              throw new IOException("Connection output is closed");
65          }
66      }
67  
68      public boolean awaitClosed(long duration)
69      {
70          return (isInputClosed() && isOutputClosed());
71      }
72  
73      public ConnectionState getConnectionState()
74      {
75          return state;
76      }
77  
78      public ConnectionState getState()
79      {
80          return state;
81      }
82  
83      public boolean isClosed()
84      {
85          return (isInputClosed() && isOutputClosed());
86      }
87  
88      public boolean isCloseInitiated()
89      {
90          return remoteCloseInitiated.get() || localCloseInitiated.get();
91      }
92  
93      public boolean isInputClosed()
94      {
95          return inputClosed.get();
96      }
97  
98      public boolean isOpen()
99      {
100         return (getState() != ConnectionState.CLOSED);
101     }
102 
103     public boolean isOutputClosed()
104     {
105         return outputClosed.get();
106     }
107 
108     /**
109      * Test for if connection should disconnect or response on a close handshake.
110      * 
111      * @param incoming
112      *            true if incoming close
113      * @param close
114      *            the close details.
115      * @return true if connection should be disconnected now, or false if response to close should be issued.
116      */
117     public boolean onCloseHandshake(boolean incoming)
118     {
119         boolean in = inputClosed.get();
120         boolean out = outputClosed.get();
121         if (incoming)
122         {
123             in = true;
124             this.inputClosed.set(true);
125 
126             if (!localCloseInitiated.get())
127             {
128                 remoteCloseInitiated.set(true);
129             }
130         }
131         else
132         {
133             out = true;
134             this.outputClosed.set(true);
135 
136             if ( !remoteCloseInitiated.get() )
137             {
138                 localCloseInitiated.set(true);
139             }
140         }
141 
142         LOG.debug("onCloseHandshake({}), input={}, output={}",incoming,in,out);
143 
144         if (in && out)
145         {
146             LOG.debug("Close Handshake satisfied, disconnecting");
147             cleanClose.set(true);
148             return true;
149         }
150 
151         return false;
152     }
153 
154     public void setConnectionState(ConnectionState connectionState)
155     {
156         this.state = connectionState;
157     }
158 
159     public void setState(ConnectionState state)
160     {
161         this.state = state;
162     }
163 
164     public boolean wasCleanClose()
165     {
166         return cleanClose.get();
167     }
168 
169     public boolean wasLocalCloseInitiated()
170     {
171         return localCloseInitiated.get();
172     }
173 
174     public boolean wasRemoteCloseInitiated()
175     {
176         return remoteCloseInitiated.get();
177     }
178 }