X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Fio%2FWriteFlusher.java;fp=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Fio%2FWriteFlusher.java;h=fccc622955218f1f5765e020415d942bc86d4162;hp=0000000000000000000000000000000000000000;hb=73ef54a38e3930a1a789cdc6b5fa23cdd4c9d086;hpb=515007c7c1351045420669d65b59c08fa46850f2 diff --git a/lib/jetty/org/eclipse/jetty/io/WriteFlusher.java b/lib/jetty/org/eclipse/jetty/io/WriteFlusher.java new file mode 100644 index 00000000..fccc6229 --- /dev/null +++ b/lib/jetty/org/eclipse/jetty/io/WriteFlusher.java @@ -0,0 +1,504 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.WritePendingException; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + +/** + * A Utility class to help implement {@link EndPoint#write(Callback, ByteBuffer...)} by calling + * {@link EndPoint#flush(ByteBuffer...)} until all content is written. + * The abstract method {@link #onIncompleteFlushed()} is called when not all content has been written after a call to + * flush and should organise for the {@link #completeWrite()} method to be called when a subsequent call to flush + * should be able to make more progress. + *
+ */
+abstract public class WriteFlusher
+{
+ private static final Logger LOG = Log.getLogger(WriteFlusher.class);
+ private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
+ private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[0];
+ private static final EnumMapPendingState
object to preserve the state
+ * and then calls {@link #onIncompleteFlushed()}. The remaining buffers will be written in {@link #completeWrite()}.
+ *
+ * If all buffers have been written it calls callback.complete().
+ *
+ * @param callback the callback to call on either failed or complete
+ * @param buffers the buffers to flush to the endpoint
+ */
+ public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
+ {
+ if (DEBUG)
+ LOG.debug("write: {} {}", this, BufferUtil.toDetailString(buffers));
+
+ if (!updateState(__IDLE,__WRITING))
+ throw new WritePendingException();
+
+ try
+ {
+ boolean flushed=_endPoint.flush(buffers);
+ if (DEBUG)
+ LOG.debug("flushed {}", flushed);
+
+ // Are we complete?
+ for (ByteBuffer b : buffers)
+ {
+ if (!flushed||BufferUtil.hasContent(b))
+ {
+ PendingState pending=new PendingState(buffers, callback);
+ if (updateState(__WRITING,pending))
+ onIncompleteFlushed();
+ else
+ fail(pending);
+ return;
+ }
+ }
+
+ // If updateState didn't succeed, we don't care as our buffers have been written
+ if (!updateState(__WRITING,__IDLE))
+ ignoreFail();
+ if (callback!=null)
+ callback.succeeded();
+ }
+ catch (IOException e)
+ {
+ if (DEBUG)
+ LOG.debug("write exception", e);
+ if (updateState(__WRITING,__IDLE))
+ {
+ if (callback!=null)
+ callback.failed(e);
+ }
+ else
+ fail(new PendingState(buffers, callback));
+ }
+ }
+
+
+ /**
+ * Complete a write that has not completed and that called {@link #onIncompleteFlushed()} to request a call to this
+ * method when a call to {@link EndPoint#flush(ByteBuffer...)} is likely to be able to progress.
+ *
+ * It tries to switch from PENDING to COMPLETING. If state transition fails, then it does nothing as the callback
+ * should have been already failed. That's because the only way to switch from PENDING outside this method is
+ * {@link #onFail(Throwable)} or {@link #onClose()}
+ */
+ public void completeWrite()
+ {
+ if (DEBUG)
+ LOG.debug("completeWrite: {}", this);
+
+ State previous = _state.get();
+
+ if (previous.getType()!=StateType.PENDING)
+ return; // failure already handled.
+
+ PendingState pending = (PendingState)previous;
+ if (!updateState(pending,__COMPLETING))
+ return; // failure already handled.
+
+ try
+ {
+ ByteBuffer[] buffers = pending.getBuffers();
+
+ boolean flushed=_endPoint.flush(buffers);
+ if (DEBUG)
+ LOG.debug("flushed {}", flushed);
+
+ // Are we complete?
+ for (ByteBuffer b : buffers)
+ {
+ if (!flushed || BufferUtil.hasContent(b))
+ {
+ if (updateState(__COMPLETING,pending))
+ onIncompleteFlushed();
+ else
+ fail(pending);
+ return;
+ }
+ }
+
+ // If updateState didn't succeed, we don't care as our buffers have been written
+ if (!updateState(__COMPLETING,__IDLE))
+ ignoreFail();
+ pending.complete();
+ }
+ catch (IOException e)
+ {
+ if (DEBUG)
+ LOG.debug("completeWrite exception", e);
+ if(updateState(__COMPLETING,__IDLE))
+ pending.fail(e);
+ else
+ fail(pending);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Notify the flusher of a failure
+ * @param cause The cause of the failure
+ * @return true if the flusher passed the failure to a {@link Callback} instance
+ */
+ public boolean onFail(Throwable cause)
+ {
+ // Keep trying to handle the failure until we get to IDLE or FAILED state
+ while(true)
+ {
+ State current=_state.get();
+ switch(current.getType())
+ {
+ case IDLE:
+ case FAILED:
+ if (DEBUG)
+ LOG.debug("ignored: {} {}", this, cause);
+ return false;
+
+ case PENDING:
+ if (DEBUG)
+ LOG.debug("failed: {} {}", this, cause);
+
+ PendingState pending = (PendingState)current;
+ if (updateState(pending,__IDLE))
+ return pending.fail(cause);
+ break;
+
+ default:
+ if (DEBUG)
+ LOG.debug("failed: {} {}", this, cause);
+
+ if (updateState(current,new FailedState(cause)))
+ return false;
+ break;
+ }
+ }
+ }
+
+ public void onClose()
+ {
+ if (_state.get()==__IDLE)
+ return;
+ onFail(new ClosedChannelException());
+ }
+
+ boolean isIdle()
+ {
+ return _state.get().getType() == StateType.IDLE;
+ }
+
+ public boolean isInProgress()
+ {
+ switch(_state.get().getType())
+ {
+ case WRITING:
+ case PENDING:
+ case COMPLETING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("WriteFlusher@%x{%s}", hashCode(), _state.get());
+ }
+}