View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.nio.ByteBuffer;
24  import java.util.concurrent.TimeoutException;
25  
26  import org.eclipse.jetty.util.BufferUtil;
27  import org.eclipse.jetty.util.Callback;
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.log.Logger;
30  import org.eclipse.jetty.util.thread.Scheduler;
31  
32  public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
33  {
34      private static final Logger LOG = Log.getLogger(AbstractEndPoint.class);
35      private final long _created=System.currentTimeMillis();
36      private final InetSocketAddress _local;
37      private final InetSocketAddress _remote;
38      private volatile Connection _connection;
39  
40      private final FillInterest _fillInterest = new FillInterest()
41      {
42          @Override
43          protected void needsFillInterest() throws IOException
44          {
45              AbstractEndPoint.this.needsFillInterest();
46          }
47      };
48  
49      private final WriteFlusher _writeFlusher = new WriteFlusher(this)
50      {
51          @Override
52          protected void onIncompleteFlush()
53          {
54              AbstractEndPoint.this.onIncompleteFlush();
55          }
56      };
57  
58      protected AbstractEndPoint(Scheduler scheduler,InetSocketAddress local,InetSocketAddress remote)
59      {
60          super(scheduler);
61          _local=local;
62          _remote=remote;
63      }
64  
65      @Override
66      public long getCreatedTimeStamp()
67      {
68          return _created;
69      }
70  
71      @Override
72      public InetSocketAddress getLocalAddress()
73      {
74          return _local;
75      }
76  
77      @Override
78      public InetSocketAddress getRemoteAddress()
79      {
80          return _remote;
81      }
82  
83      @Override
84      public Connection getConnection()
85      {
86          return _connection;
87      }
88  
89      @Override
90      public void setConnection(Connection connection)
91      {
92          _connection = connection;
93      }
94  
95      @Override
96      public boolean isOptimizedForDirectBuffers()
97      {
98          return false;
99      }
100 
101     @Override
102     public void onOpen()
103     {
104         if (LOG.isDebugEnabled())
105             LOG.debug("onOpen {}",this);
106         super.onOpen();
107     }
108 
109     @Override
110     public void close()
111     {
112         onClose();
113         _writeFlusher.onClose();
114         _fillInterest.onClose();
115     }
116 
117     protected void close(Throwable failure)
118     {
119         onClose();
120         _writeFlusher.onFail(failure);
121         _fillInterest.onFail(failure);
122     }
123 
124     @Override
125     public void fillInterested(Callback callback)
126     {
127         notIdle();
128         _fillInterest.register(callback);
129     }
130 
131     @Override
132     public boolean tryFillInterested(Callback callback)
133     {
134         notIdle();
135         return _fillInterest.tryRegister(callback);
136     }
137 
138     @Override
139     public boolean isFillInterested()
140     {
141         return _fillInterest.isInterested();
142     }
143 
144     @Override
145     public void write(Callback callback, ByteBuffer... buffers) throws IllegalStateException
146     {
147         _writeFlusher.write(callback, buffers);
148     }
149 
150     protected abstract void onIncompleteFlush();
151 
152     protected abstract void needsFillInterest() throws IOException;
153 
154     public FillInterest getFillInterest()
155     {
156         return _fillInterest;
157     }
158 
159     protected WriteFlusher getWriteFlusher()
160     {
161         return _writeFlusher;
162     }
163 
164     @Override
165     protected void onIdleExpired(TimeoutException timeout)
166     {
167         Connection connection = _connection;
168         if (connection != null && !connection.onIdleExpired())
169             return;
170 
171         boolean output_shutdown=isOutputShutdown();
172         boolean input_shutdown=isInputShutdown();
173         boolean fillFailed = _fillInterest.onFail(timeout);
174         boolean writeFailed = _writeFlusher.onFail(timeout);
175 
176         // If the endpoint is half closed and there was no fill/write handling, then close here.
177         // This handles the situation where the connection has completed its close handling
178         // and the endpoint is half closed, but the other party does not complete the close.
179         // This perhaps should not check for half closed, however the servlet spec case allows
180         // for a dispatched servlet or suspended request to extend beyond the connections idle
181         // time.  So if this test would always close an idle endpoint that is not handled, then
182         // we would need a mode to ignore timeouts for some HTTP states
183         if (isOpen() && (output_shutdown || input_shutdown) && !(fillFailed || writeFailed))
184             close();
185         else
186             LOG.debug("Ignored idle endpoint {}",this);
187     }
188 
189     @Override
190     public void upgrade(Connection newConnection)
191     {
192         Connection old_connection = getConnection();
193 
194         if (LOG.isDebugEnabled())
195             LOG.debug("{} upgrading from {} to {}", this, old_connection, newConnection);
196 
197         ByteBuffer prefilled = (old_connection instanceof Connection.UpgradeFrom)
198                 ?((Connection.UpgradeFrom)old_connection).onUpgradeFrom():null;
199         old_connection.onClose();
200         old_connection.getEndPoint().setConnection(newConnection);
201 
202         if (newConnection instanceof Connection.UpgradeTo)
203             ((Connection.UpgradeTo)newConnection).onUpgradeTo(prefilled);
204         else if (BufferUtil.hasContent(prefilled))
205             throw new IllegalStateException();
206 
207         newConnection.onOpen();
208     }
209 
210     @Override
211     public String toString()
212     {
213         Class<?> c=getClass();
214         String name=c.getSimpleName();
215         while (name.length()==0 && c.getSuperclass()!=null)
216         {
217             c=c.getSuperclass();
218             name=c.getSimpleName();
219         }
220 
221         Connection connection = getConnection();
222         return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d/%d,%s@%x}",
223                 name,
224                 hashCode(),
225                 getRemoteAddress(),
226                 getLocalAddress().getPort(),
227                 isOpen()?"Open":"CLOSED",
228                 isInputShutdown()?"ISHUT":"in",
229                 isOutputShutdown()?"OSHUT":"out",
230                 _fillInterest.toStateString(),
231                 _writeFlusher.toStateString(),
232                 getIdleFor(),
233                 getIdleTimeout(),
234                 connection == null ? null : connection.getClass().getSimpleName(),
235                 connection == null ? 0 : connection.hashCode());
236     }
237 }