View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 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.io;
20  
21  import java.util.List;
22  import java.util.concurrent.CopyOnWriteArrayList;
23  import java.util.concurrent.Executor;
24  import java.util.concurrent.TimeoutException;
25  import java.util.concurrent.atomic.AtomicReference;
26  
27  import org.eclipse.jetty.util.Callback;
28  import org.eclipse.jetty.util.ExecutorCallback;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  
32  /**
33   * <p>A convenience base implementation of {@link Connection}.</p>
34   * <p>This class uses the capabilities of the {@link EndPoint} API to provide a
35   * more traditional style of async reading.  A call to {@link #fillInterested()}
36   * will schedule a callback to {@link #onFillable()} or {@link #onFillInterestedFailed(Throwable)}
37   * as appropriate.</p>
38   */
39  public abstract class AbstractConnection implements Connection
40  {
41      private static final Logger LOG = Log.getLogger(AbstractConnection.class);
42  
43      private final List<Listener> listeners = new CopyOnWriteArrayList<>();
44      private final AtomicReference<State> _state = new AtomicReference<>(State.IDLE);
45      private final long _created=System.currentTimeMillis();
46      private final EndPoint _endPoint;
47      private final Executor _executor;
48      private final Callback<Void> _readCallback;
49      private int _inputBufferSize=2048;
50  
51      public AbstractConnection(EndPoint endp, Executor executor)
52      {
53          this(endp,executor,true);
54      }
55      
56      public AbstractConnection(EndPoint endp, Executor executor, final boolean executeOnfillable)
57      {
58          if (executor == null)
59              throw new IllegalArgumentException("Executor must not be null!");
60          _endPoint = endp;
61          _executor = executor;
62          _readCallback = new ExecutorCallback<Void>(executor,0)
63          {
64              @Override
65              public void completed(Void context)
66              {
67                  if (executeOnfillable)
68                      super.completed(context);
69                  else
70                      onCompleted(context);
71              }
72              
73              @Override
74              protected void onCompleted(Void context)
75              {
76                  if (_state.compareAndSet(State.INTERESTED,State.FILLING))
77                  {
78                      try
79                      {
80                          onFillable();
81                      }
82                      finally
83                      {
84                          loop:while(true)
85                          {
86                              switch(_state.get())
87                              {
88                                  case IDLE:
89                                  case INTERESTED:
90                                      throw new IllegalStateException();
91  
92                                  case FILLING:
93                                      if (_state.compareAndSet(State.FILLING,State.IDLE))
94                                          break loop;
95                                      break;
96  
97                                  case FILLING_INTERESTED:
98                                      if (_state.compareAndSet(State.FILLING_INTERESTED,State.INTERESTED))
99                                      {
100                                         getEndPoint().fillInterested(null, _readCallback);
101                                         break loop;
102                                     }
103                                     break;
104                             }
105                         }
106 
107                     }
108                 }
109                 else
110                     LOG.warn(new Throwable());
111             }
112 
113             @Override
114             protected void onFailed(Void context, Throwable x)
115             {
116                 onFillInterestedFailed(x);
117             }
118 
119             @Override
120             public String toString()
121             {
122                 return String.format("AC.ExReadCB@%x", AbstractConnection.this.hashCode());
123             }
124         };
125     }
126 
127     @Override
128     public void addListener(Listener listener)
129     {
130         listeners.add(listener);
131     }
132 
133     public int getInputBufferSize()
134     {
135         return _inputBufferSize;
136     }
137 
138     public void setInputBufferSize(int inputBufferSize)
139     {
140         _inputBufferSize = inputBufferSize;
141     }
142 
143     protected Executor getExecutor()
144     {
145         return _executor;
146     }
147     
148     /**
149      * <p>Utility method to be called to register read interest.</p>
150      * <p>After a call to this method, {@link #onFillable()} or {@link #onFillInterestedFailed(Throwable)}
151      * will be called back as appropriate.</p>
152      * @see #onFillable()
153      */
154     public void fillInterested()
155     {
156         LOG.debug("fillInterested {}",this);
157 
158         loop:while(true)
159         {
160             switch(_state.get())
161             {
162                 case IDLE:
163                     if (_state.compareAndSet(State.IDLE,State.INTERESTED))
164                     {
165                         getEndPoint().fillInterested(null, _readCallback);
166                         break loop;
167                     }
168                     break;
169 
170                 case FILLING:
171                     if (_state.compareAndSet(State.FILLING,State.FILLING_INTERESTED))
172                         break loop;
173                     break;
174 
175                 case FILLING_INTERESTED:
176                 case INTERESTED:
177                     break loop;
178             }
179         }
180     }
181 
182     /**
183      * <p>Callback method invoked when the endpoint is ready to be read.</p>
184      * @see #fillInterested()
185      */
186     public abstract void onFillable();
187 
188     /**
189      * <p>Callback method invoked when the endpoint failed to be ready to be read.</p>
190      * @param cause the exception that caused the failure
191      */
192     protected void onFillInterestedFailed(Throwable cause)
193     {
194         LOG.debug("{} onFillInterestedFailed {}", this, cause);
195         if (_endPoint.isOpen())
196         {
197             boolean close = true;
198             if (cause instanceof TimeoutException)
199                 close = onReadTimeout();
200             if (close)
201             {
202                 if (_endPoint.isOutputShutdown())
203                     _endPoint.close();
204                 else
205                     _endPoint.shutdownOutput();
206             }
207         }
208     }
209 
210     /**
211      * <p>Callback method invoked when the endpoint failed to be ready to be read after a timeout</p>
212      * @return true to signal that the endpoint must be closed, false to keep the endpoint open
213      */
214     protected boolean onReadTimeout()
215     {
216         return true;
217     }
218 
219     // TODO remove this when open/close refactored
220     final AtomicReference<Throwable> _opened = new AtomicReference<>(null);
221     @Override
222     public void onOpen()
223     {
224         LOG.debug("onOpen {}", this);
225 
226         for (Listener listener : listeners)
227             listener.onOpened(this);
228 
229         if (!_opened.compareAndSet(null,new Throwable()))
230         {
231             LOG.warn("ALREADY OPENED ", _opened.get());
232             LOG.warn("EXTRA OPEN AT ",new Throwable());
233         }
234     }
235 
236     @Override
237     public void onClose()
238     {
239         LOG.debug("onClose {}",this);
240 
241         for (Listener listener : listeners)
242             listener.onClosed(this);
243     }
244 
245     @Override
246     public EndPoint getEndPoint()
247     {
248         return _endPoint;
249     }
250 
251     @Override
252     public void close()
253     {
254         getEndPoint().close();
255     }
256 
257     @Override
258     public int getMessagesIn()
259     {
260         return -1;
261     }
262 
263     @Override
264     public int getMessagesOut()
265     {
266         return -1;
267     }
268 
269     @Override
270     public long getBytesIn()
271     {
272         return -1;
273     }
274 
275     @Override
276     public long getBytesOut()
277     {
278         return -1;
279     }
280 
281     @Override
282     public long getCreatedTimeStamp()
283     {
284         return _created;
285     }
286 
287     @Override
288     public String toString()
289     {
290         return String.format("%s@%x{%s}", getClass().getSimpleName(), hashCode(), _state.get());
291     }
292 
293     private enum State
294     {
295         IDLE, INTERESTED, FILLING, FILLING_INTERESTED
296     }
297 }