1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
34
35
36
37
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
150
151
152
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
184
185
186 public abstract void onFillable();
187
188
189
190
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
212
213
214 protected boolean onReadTimeout()
215 {
216 return true;
217 }
218
219
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 }