/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2008-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.grizzly.filterchain;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.grizzly.Appendable;
import org.glassfish.grizzly.Appender;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.CompletionHandler;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Context;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.IOEvent;
import org.glassfish.grizzly.ProcessorExecutor;
import org.glassfish.grizzly.ReadResult;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.WriteResult;
import org.glassfish.grizzly.asyncqueue.MessageCloner;
import org.glassfish.grizzly.attributes.AttributeHolder;
import org.glassfish.grizzly.attributes.AttributeStorage;
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.MemoryManager;
import org.glassfish.grizzly.utils.Holder;
/**
* {@link FilterChain} {@link Context} implementation.
*
* @see Context
* @see FilterChain
*
* @author Alexey Stashok
*/
@SuppressWarnings("deprecation")
public final class FilterChainContext implements AttributeStorage {
private static final Logger logger = Grizzly.logger(FilterChainContext.class);
public enum State {
RUNNING, SUSPEND
}
public enum Operation {
NONE, ACCEPT, CONNECT, READ, WRITE, EVENT, CLOSE
}
private static final ThreadCache.CachedTypeIndex<FilterChainContext> CACHE_IDX =
ThreadCache.obtainIndex(FilterChainContext.class, 8);
public static FilterChainContext create(final Connection connection) {
FilterChainContext context = ThreadCache.takeFromCache(CACHE_IDX);
if (context == null) {
context = new FilterChainContext();
}
context.setConnection(connection);
context.getTransportContext().isBlocking = connection.isBlocking();
return context;
}
public static final int NO_FILTER_INDEX = Integer.MIN_VALUE;
/**
* Cached {@link NextAction} instance for "Invoke action" implementation
*/
private static final NextAction INVOKE_ACTION = new InvokeAction();
/**
* Cached {@link NextAction} instance for "Stop action" implementation
*/
private static final NextAction STOP_ACTION = new StopAction();
/**
* Cached {@link NextAction} instance for "Suspend action" implementation
*/
private static final NextAction SUSPEND_ACTION = new SuspendAction();
/**
* Cached {@link NextAction} instance for "Rerun filter action" implementation
*/
private static final NextAction RERUN_FILTER_ACTION = new RerunFilterAction();
final InternalContextImpl internalContext = new InternalContextImpl(this);
final TransportContext transportFilterContext = new TransportContext();
/**
* Context task state
*/
private volatile State state;
private Operation operation = Operation.NONE;
/**
* {@link CompletionHandler}, which will be notified, when operation will be
* complete. For WRITE it means the data will be written on wire, for other
* operations - the last Filter has finished the processing.
*/
protected CompletionHandler<FilterChainContext> operationCompletionHandler;
private final Runnable contextRunnable;
/**
* Context associated message
*/
private Object message;
/**
* Context associated event, if EVENT operation
*/
protected FilterChainEvent event;
/**
* {@link Holder}, which contains address, associated with the
* current {@link org.glassfish.grizzly.IOEvent} processing.
*/
private Holder<?> addressHolder;
NextAction predefinedNextAction;
/**
* Index of the currently executing {@link Filter} in
* the {@link FilterChainContext} list.
*/
private int filterIdx;
private int startIdx;
private int endIdx;
private final StopAction cachedStopAction = new StopAction();
private final InvokeAction cachedInvokeAction = new InvokeAction();
private final List<CompletionListener> completionListeners =
new ArrayList<CompletionListener>(2);
private final List<CopyListener> copyListeners =
new ArrayList<CopyListener>(2);
public FilterChainContext() {
filterIdx = NO_FILTER_INDEX;
contextRunnable = new Runnable() {
@Override
public void run() {
try {
if (state == State.SUSPEND) {
state = State.RUNNING;
}
ProcessorExecutor.execute(FilterChainContext.this.internalContext);
} catch (Exception e) {
logger.log(Level.FINE, "Exception during running Processor", e);
}
}
};
}
/**
* Suspend processing of the current task
*/
public Runnable suspend() {
internalContext.suspend();
this.state = State.SUSPEND;
return getRunnable();
}
/**
* Resume processing of the current task
*/
public void resume() {
internalContext.resume();
getRunnable().run();
}
/**
* Resume processing of the current task starting from the Filter, which
* follows the Filter, which suspend the processing.
*/
public void resumeNext() {
resume(getInvokeAction());
}
/**
* Resume the current FilterChain task processing by completing the current
* {@link Filter} (the Filter, which suspended the processing) with the
* passed {@link NextAction}.
*
* @param nextAction the {@link NextAction}, based on which {@link FilterChain}
* will continue processing.
*/
public void resume(final NextAction nextAction) {
internalContext.resume();
try {
if (state == State.SUSPEND) {
state = State.RUNNING;
}
predefinedNextAction = nextAction;
ProcessorExecutor.execute(internalContext);
} catch (Exception e) {
logger.log(Level.FINE, "Exception during running Processor", e);
}
}
/**
* This method invocation suggests the {@link FilterChain} to create a
* copy of this {@link FilterChainContext} and resume/fork its execution
* starting from the current {@link Filter}.
*/
public void fork() {
fork(null);
}
/**
* This method invocation suggests the {@link FilterChain} to create a
* copy of this {@link FilterChainContext} and resume/fork its execution
* starting from the current {@link Filter}.
* <p/>
* If <code>nextAction</code> parameter is not null - then its value is treated
* as a result of the current {@link Filter} execution on the forked
* {@link FilterChain} processing. So during the forked {@link FilterChain}
* processing the current {@link Filter} will not be invoked, but
* the processing will be continued as if the current {@link Filter}
* returned <code>nextAction</code> as a result.
* For example if we call <code>fork(ctx.getInvokeAction());</code> the forked
* {@link FilterChain} processing will start with the next {@link Filter} in
* chain.
*/
public void fork(final NextAction nextAction) {
try {
predefinedNextAction = getForkAction(nextAction);
ProcessorExecutor.execute(internalContext);
} catch (Exception e) {
logger.log(Level.FINE, "Exception during running Processor", e);
}
}
/**
* Get the current processing task state.
* @return the current processing task state.
*/
public State state() {
return state;
}
public int nextFilterIdx() {
return ++filterIdx;
}
public int previousFilterIdx() {
return --filterIdx;
}
public int getFilterIdx() {
return filterIdx;
}
public void setFilterIdx(int index) {
this.filterIdx = index;
}
public int getStartIdx() {
return startIdx;
}
public void setStartIdx(int startIdx) {
this.startIdx = startIdx;
}
public int getEndIdx() {
return endIdx;
}
public void setEndIdx(int endIdx) {
this.endIdx = endIdx;
}
/**
* Get {@link FilterChain}, which runs the {@link Filter}.
*
* @return {@link FilterChain}, which runs the {@link Filter}.
*/
public FilterChain getFilterChain() {
return (FilterChain) internalContext.getProcessor();
}
/**
* Get the {@link Connection}, associated with the current processing.
*
* @return {@link Connection} object, associated with the current processing.
*/
public Connection getConnection() {
return internalContext.getConnection();
}
/**
* Set the {@link Connection}, associated with the current processing.
*
* @param connection {@link Connection} object, associated with the current processing.
*/
void setConnection(final Connection connection) {
internalContext.setConnection(connection);
}
/**
* Get message object, associated with the current processing.
*
* Usually {@link FilterChain} represents sequence of parser and process
* {@link Filter}s. Each parser can change the message representation until
* it will come to processor {@link Filter}.
*
* @return message object, associated with the current processing.
*/
@SuppressWarnings("unchecked")
public <T> T getMessage() {
return (T) message;
}
/**
* Set message object, associated with the current processing.
*
* Usually {@link FilterChain} represents sequence of parser and process
* {@link Filter}s. Each parser can change the message representation until
* it will come to processor {@link Filter}.
*
* @param message message object, associated with the current processing.
*/
public void setMessage(Object message) {
this.message = message;
}
/**
* Get a {@link Holder}, which contains address, associated with the
* current {@link org.glassfish.grizzly.IOEvent} processing.
*
* When we process {@link org.glassfish.grizzly.IOEvent#READ} event - it represents sender address,
* or when process {@link org.glassfish.grizzly.IOEvent#WRITE} - address of receiver.
*
* @return {@link Holder}, which contains address, associated with the
* current {@link org.glassfish.grizzly.IOEvent} processing.
*/
public Holder<?> getAddressHolder() {
return addressHolder;
}
/**
* Set address, associated with the current {@link org.glassfish.grizzly.IOEvent} processing.
* When we process {@link org.glassfish.grizzly.IOEvent#READ} event - it represents sender address,
* or when process {@link org.glassfish.grizzly.IOEvent#WRITE} - address of receiver.
*
* @param addressHolder {@link Holder}, which contains address, associated with the current {@link org.glassfish.grizzly.IOEvent} processing.
*/
public void setAddressHolder(final Holder<?> addressHolder) {
this.addressHolder = addressHolder;
}
/**
* Get address, associated with the current {@link org.glassfish.grizzly.IOEvent} processing.
* When we process {@link org.glassfish.grizzly.IOEvent#READ} event - it represents sender address,
* or when process {@link org.glassfish.grizzly.IOEvent#WRITE} - address of receiver.
*
* @return address, associated with the current {@link org.glassfish.grizzly.IOEvent} processing.
*/
public Object getAddress() {
return addressHolder != null ? addressHolder.get() : null;
}
/**
* Set address, associated with the current {@link org.glassfish.grizzly.IOEvent} processing.
* When we process {@link org.glassfish.grizzly.IOEvent#READ} event - it represents sender address,
* or when process {@link org.glassfish.grizzly.IOEvent#WRITE} - address of receiver.
*
* @param address address, associated with the current {@link org.glassfish.grizzly.IOEvent} processing.
*/
public void setAddress(final Object address) {
addressHolder = Holder.<Object>staticHolder(address);
}
protected final Runnable getRunnable() {
return contextRunnable;
}
/**
* Get the {@link TransportFilter} related context.
*
* @return {@link TransportFilter}.
*/
public TransportContext getTransportContext() {
return transportFilterContext;
}
/**
* Get the general Grizzly {@link Context} this filter context wraps.
* @return the general Grizzly {@link Context} this filter context wraps.
*/
public final Context getInternalContext() {
return internalContext;
}
Operation getOperation() {
return operation;
}
void setOperation(Operation operation) {
this.operation = operation;
}
/**
* Get {@link NextAction} implementation, which instructs {@link FilterChain} to
* process next {@link Filter} in chain.
*
* Normally, after receiving this instruction from {@link Filter},
* {@link FilterChain} executes next filter.
*
* @return {@link NextAction} implementation, which instructs {@link FilterChain} to
* process next {@link Filter} in chain.
*/
public NextAction getInvokeAction() {
return INVOKE_ACTION;
}
/**
* Get {@link NextAction} implementation, which instructs {@link FilterChain} to
* process next {@link Filter} in chain.
*
* Normally, after receiving this instruction from {@link Filter},
* {@link FilterChain} executes next filter.
*
* @param unparsedChunk signals, that there is some unparsed data remaining
* in the source message, so {@link FilterChain} could be rerun.
*
* @return {@link NextAction} implementation, which instructs {@link FilterChain} to
* process next {@link Filter} in chain.
*/
public NextAction getInvokeAction(final Object unparsedChunk) {
if (unparsedChunk == null) {
return INVOKE_ACTION;
}
cachedInvokeAction.setUnparsedChunk(unparsedChunk);
return cachedInvokeAction;
}
/**
* Get {@link NextAction} implementation, which instructs {@link FilterChain} to
* process next {@link Filter} in chain.
*
* Normally, after receiving this instruction from {@link Filter},
* {@link FilterChain} executes next filter.
*
* @param incompleteChunk signals, that there is a data chunk remaining,
* which doesn't represent complete message. As more data becomes available
* but before {@link FilterChain} calls the current {@link Filter},
* it will check if the {@link Filter} has any data stored after the
* last invocation. If a remainder is present it will append the new data
* to the stored one and pass the result as the FilterChainContext message.
*
* @param appender the {@link org.glassfish.grizzly.Appender}, which knows
* how to append chunks of type <code><E></code>.
*
* @return {@link NextAction} implementation, which instructs {@link FilterChain} to
* process next {@link Filter} in chain.
*
* @throws IllegalArgumentException if appender is <code>null</code> and
* remainder's type is not {@link Buffer} or {@link Appendable}.
*/
@SuppressWarnings("unchecked")
public <E> NextAction getInvokeAction(final E incompleteChunk,
org.glassfish.grizzly.Appender<E> appender) {
if (incompleteChunk == null) {
return INVOKE_ACTION;
}
if (appender == null && !(incompleteChunk instanceof Appendable)) {
if (incompleteChunk instanceof Buffer) {
appender = (Appender<E>) Buffers.getBufferAppender(true);
} else {
throw new IllegalArgumentException("Remainder has to be either "
+ Buffer.class.getName() + " or " + Appendable.class.getName() +
" but was " + incompleteChunk.getClass().getName());
}
}
cachedInvokeAction.setIncompleteChunk(incompleteChunk, appender);
return cachedInvokeAction;
}
/**
* Get {@link NextAction} implementation, which instructs {@link FilterChain}
* to stop executing phase.
*
* @return {@link NextAction} implementation, which instructs {@link FilterChain}
* to stop executing phase.
*/
public NextAction getStopAction() {
return STOP_ACTION;
}
/**
* Get {@link NextAction} implementation, which instructs {@link FilterChain}
* stop executing phase.
*
* @param incompleteChunk signals, that there is a data chunk remaining,
* which doesn't represent complete message. As more data becomes available
* but before {@link FilterChain} calls the current {@link Filter},
* it will check if the {@link Filter} has any data stored after the
* last invocation. If a remainder is present it will append the new data
* to the stored one and pass the result as the FilterChainContext message.
*
* @return {@link NextAction} implementation, which instructs {@link FilterChain}
* to stop executing phase.
*
* @throws IllegalArgumentException if remainder's type is not {@link Buffer} or
* {@link Appendable}.
*/
public NextAction getStopAction(final Object incompleteChunk) {
if (incompleteChunk instanceof Buffer) {
return getStopAction((Buffer) incompleteChunk,
Buffers.getBufferAppender(true));
}
return getStopAction(incompleteChunk, null);
}
/**
* Get {@link NextAction} implementation, which instructs {@link FilterChain}
* stop executing phase.
*
* @param incompleteChunk signals, that there is a data chunk remaining,
* which doesn't represent complete message. As more data becomes available
* but before {@link FilterChain} calls the current {@link Filter},
* it will check if the {@link Filter} has any data stored after the
* last invocation. If a remainder is present it will append the new data
* to the stored one and pass the result as the FilterChainContext message.
*
* @param appender the {@link org.glassfish.grizzly.Appender}, which knows
* how to append chunks of type <code><E></code>.
*
* @return {@link NextAction} implementation, which instructs {@link FilterChain}
* to stop executing phase.
*
* @throws IllegalArgumentException if appender is <code>null</code> and
* remainder's type is not {@link Buffer} or {@link Appendable}.
*/
public <E> NextAction getStopAction(final E incompleteChunk,
final org.glassfish.grizzly.Appender<E> appender) {
if (incompleteChunk == null) {
return STOP_ACTION;
}
if (appender == null && !(incompleteChunk instanceof Appendable)) {
throw new IllegalArgumentException("Remainder has to be either "
+ Buffer.class.getName() + " or " + Appendable.class.getName() +
" but was " + incompleteChunk.getClass().getName());
}
cachedStopAction.setIncompleteChunk(incompleteChunk, appender);
return cachedStopAction;
}
/**
* @return {@link NextAction} implementation, which suggests the {@link FilterChain}
* to forget about the current {@link FilterChainContext}, create a copy of it
* and continue/fork {@link FilterChain} processing using the copied
* {@link FilterChainContext} starting from the current {@link Filter}.
*/
public NextAction getForkAction() {
return getForkAction(null);
}
/**
* @return {@link NextAction} implementation, which suggests the {@link FilterChain}
* to forget about the current {@link FilterChainContext}, create a copy of it
* and continue/fork {@link FilterChain} processing using the copied
* {@link FilterChainContext} starting from the current {@link Filter}.
* <p/>
* If <code>nextAction</code> parameter is not null - then its value is treated
* as a result of the current {@link Filter} execution on the forked
* {@link FilterChain} processing. So during the forked {@link FilterChain}
* processing the current {@link Filter} will not be invoked, but
* the processing will be continued as if the current {@link Filter}
* returned <code>nextAction</code> as a result.
* For example if we call <code>fork(ctx.getInvokeAction());</code> the forked
* {@link FilterChain} processing will start with the next {@link Filter} in
* chain.
*/
public NextAction getForkAction(final NextAction nextAction) {
final FilterChainContext contextCopy = copy();
// Copy doesn't copy address
contextCopy.addressHolder = addressHolder;
contextCopy.predefinedNextAction = nextAction;
return new ForkAction(contextCopy);
}
/**
* @return {@link NextAction} implementation, which instructs the {@link FilterChain}
* to suspend the current {@link FilterChainContext} and invoke similar logic
* as instructed by {@link StopAction} with a clean {@link FilterChainContext}.
*
* @deprecated use {@link #getForkAction()}
*/
public NextAction getSuspendingStopAction() {
return getForkAction();
}
/**
* Get {@link NextAction}, which instructs {@link FilterChain} to suspend filter
* chain execution.
*
* @return {@link NextAction}, which instructs {@link FilterChain} to suspend
* filter chain execution.
*/
public NextAction getSuspendAction() {
return SUSPEND_ACTION;
}
/**
* Get {@link NextAction}, which instructs {@link FilterChain} to rerun the
* filter.
*
* @return {@link NextAction}, which instructs {@link FilterChain} to rerun the
* filter.
*/
public NextAction getRerunFilterAction() {
return RERUN_FILTER_ACTION;
}
/**
* <p>
* Performs a blocking read.
* </p>
*
* @return the result of the read operation.
*
* @throws IOException if an I/O error occurs.
*/
public ReadResult read() throws IOException {
final FilterChainContext newContext =
getFilterChain().obtainFilterChainContext(getConnection());
newContext.operation = Operation.READ;
newContext.transportFilterContext.configureBlocking(true);
newContext.startIdx = 0;
newContext.filterIdx = 0;
newContext.endIdx = filterIdx;
getAttributes().copyTo(newContext.getAttributes());
final ReadResult rr = getFilterChain().read(newContext);
newContext.completeAndRecycle();
return rr;
}
public void write(final Object message) {
write(null, message, null, null, null,
transportFilterContext.isBlocking());
}
public void write(final Object message, final boolean blocking) {
write(null, message, null, null, null, blocking);
}
public void write(final Object message,
final CompletionHandler<WriteResult> completionHandler) {
write(null,
message,
completionHandler,
null, null,
transportFilterContext.isBlocking());
}
public void write(final Object message,
final CompletionHandler<WriteResult> completionHandler,
final boolean blocking) {
write(null, message, completionHandler, null, null, blocking);
}
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler) {
write(address,
message,
completionHandler,
null,
null,
transportFilterContext.isBlocking());
}
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler,
final boolean blocking) {
write(address, message, completionHandler, null, null, blocking);
}
@Deprecated
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler,
final org.glassfish.grizzly.asyncqueue.PushBackHandler pushBackHandler) {
write(address,
message,
completionHandler,
pushBackHandler,
transportFilterContext.isBlocking());
}
@Deprecated
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler,
final org.glassfish.grizzly.asyncqueue.PushBackHandler pushBackHandler,
final boolean blocking) {
write(address,
message,
completionHandler,
pushBackHandler,
null,
blocking);
}
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler,
final MessageCloner cloner) {
write(address,
message,
completionHandler,
null,
cloner,
transportFilterContext.isBlocking());
}
@Deprecated
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler,
final org.glassfish.grizzly.asyncqueue.PushBackHandler pushBackHandler,
final MessageCloner cloner) {
write(address,
message,
completionHandler,
pushBackHandler,
cloner,
transportFilterContext.isBlocking());
}
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler,
final MessageCloner cloner,
final boolean blocking) {
write(address,
message,
completionHandler,
null,
cloner,
blocking);
}
@Deprecated
public void write(final Object address,
final Object message,
final CompletionHandler<WriteResult> completionHandler,
final org.glassfish.grizzly.asyncqueue.PushBackHandler pushBackHandler,
final MessageCloner cloner,
final boolean blocking) {
final FilterChainContext newContext =
getFilterChain().obtainFilterChainContext(getConnection());
newContext.operation = Operation.WRITE;
newContext.transportFilterContext.configureBlocking(blocking);
newContext.message = message;
newContext.addressHolder = address == null ? addressHolder : Holder.<Object>staticHolder(address);
newContext.transportFilterContext.completionHandler = completionHandler;
newContext.transportFilterContext.pushBackHandler = pushBackHandler;
newContext.transportFilterContext.cloner = cloner;
newContext.startIdx = filterIdx - 1;
newContext.filterIdx = filterIdx - 1;
newContext.endIdx = -1;
getAttributes().copyTo(newContext.getAttributes());
ProcessorExecutor.execute(newContext.internalContext);
}
public void flush(final CompletionHandler completionHandler) {
final FilterChainContext newContext =
getFilterChain().obtainFilterChainContext(getConnection());
newContext.operation = Operation.EVENT;
newContext.event = TransportFilter.createFlushEvent(completionHandler);
newContext.transportFilterContext.configureBlocking(transportFilterContext.isBlocking());
newContext.addressHolder = addressHolder;
newContext.startIdx = filterIdx - 1;
newContext.filterIdx = filterIdx - 1;
newContext.endIdx = -1;
getAttributes().copyTo(newContext.getAttributes());
ProcessorExecutor.execute(newContext.internalContext);
}
public void notifyUpstream(final FilterChainEvent event) {
notifyUpstream(event, null);
}
public void notifyUpstream(final FilterChainEvent event,
final CompletionHandler<FilterChainContext> completionHandler) {
final FilterChainContext newContext =
getFilterChain().obtainFilterChainContext(getConnection());
newContext.setOperation(Operation.EVENT);
newContext.event = event;
newContext.addressHolder = addressHolder;
newContext.startIdx = filterIdx + 1;
newContext.filterIdx = filterIdx + 1;
newContext.endIdx = endIdx;
getAttributes().copyTo(newContext.getAttributes());
newContext.operationCompletionHandler = completionHandler;
ProcessorExecutor.execute(newContext.internalContext);
}
public void notifyDownstream(final FilterChainEvent event) {
notifyDownstream(event, null);
}
public void notifyDownstream(final FilterChainEvent event,
final CompletionHandler<FilterChainContext> completionHandler) {
final FilterChainContext newContext =
getFilterChain().obtainFilterChainContext(getConnection());
newContext.setOperation(Operation.EVENT);
newContext.event = event;
newContext.addressHolder = addressHolder;
newContext.startIdx =filterIdx - 1;
newContext.filterIdx = filterIdx - 1;
newContext.endIdx = -1;
getAttributes().copyTo(newContext.getAttributes());
newContext.operationCompletionHandler = completionHandler;
ProcessorExecutor.execute(newContext.internalContext);
}
public void fail(final Throwable error) {
getFilterChain().fail(this, error);
}
/**
* {@inheritDoc}
*/
@Override
public AttributeHolder getAttributes() {
return internalContext.getAttributes();
}
/**
* Add the {@link CompletionListener}, which will be notified, when
* this {@link FilterChainContext} processing will be completed.
*
* @param listener the {@link CompletionListener}, which will be notified, when
* this {@link FilterChainContext} processing will be completed.
*/
public final void addCompletionListener(final CompletionListener listener) {
completionListeners.add(listener);
}
/**
* Remove the {@link CompletionListener}.
*
* @param listener the {@link CompletionListener} to be removed.
* @return <tt>true</tt>, if the listener was removed from the list, or
* <tt>false</tt>, if the listener wasn't on the list.
*/
public final boolean removeCompletionListener(final CompletionListener listener) {
return completionListeners.remove(listener);
}
/**
* Add the {@link CopyListener}, which will be notified, right after
* this {@link FilterChainContext#copy()} is called.
*
* @param listener the {@link CopyListener}, which will be notified, right
* after this {@link FilterChainContext#copy()} is called.
*/
public final void addCopyListener(final CopyListener listener) {
copyListeners.add(listener);
}
/**
* Remove the {@link CopyListener}.
*
* @param listener the {@link CopyListener} to be removed.
* @return <tt>true</tt>, if the listener was removed from the list, or
* <tt>false</tt>, if the listener wasn't on the list.
*/
public final boolean removeCopyListener(final CopyListener listener) {
return copyListeners.remove(listener);
}
/**
* <p>A simple alias for <code>FilterChainContext.getConnection().getTransport().getMemoryManager()</code>.
*
* @return the {@link MemoryManager} associated with the {@link Connection}
* of this <code>FilterChainContext</code>.
*/
public final MemoryManager getMemoryManager() {
return (getConnection().getTransport().getMemoryManager());
}
public FilterChainContext copy() {
final FilterChain p = getFilterChain();
final FilterChainContext newContext =
p.obtainFilterChainContext(getConnection());
newContext.setOperation(getOperation());
internalContext.softCopyTo(newContext.internalContext);
newContext.setStartIdx(getStartIdx());
newContext.setEndIdx(getEndIdx());
newContext.setFilterIdx(getFilterIdx());
getAttributes().copyTo(newContext.getAttributes());
notifyCopy(this, newContext, copyListeners);
return newContext;
}
/**
* Release the context associated resources.
*/
public void reset() {
cachedInvokeAction.reset();
cachedStopAction.reset();
message = null;
event = null;
addressHolder = null;
filterIdx = NO_FILTER_INDEX;
state = State.RUNNING;
operationCompletionHandler = null;
operation = Operation.NONE;
internalContext.reset();
transportFilterContext.reset();
copyListeners.clear();
predefinedNextAction = null;
}
public void completeAndRecycle() {
notifyComplete(this, completionListeners);
reset();
ThreadCache.putToCache(CACHE_IDX, this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(384);
sb.append("FilterChainContext [");
sb.append("connection=").append(getConnection());
sb.append(", operation=").append(getOperation());
sb.append(", message=").append(getMessage());
sb.append(", address=").append(getAddress());
sb.append(']');
return sb.toString();
}
static Operation ioEvent2Operation(final IOEvent ioEvent) {
switch(ioEvent) {
case READ: return Operation.READ;
case WRITE: return Operation.WRITE;
case ACCEPTED: return Operation.ACCEPT;
case CONNECTED: return Operation.CONNECT;
case CLOSED: return Operation.CLOSE;
default: return Operation.NONE;
}
}
@SuppressWarnings("deprecation")
public static final class TransportContext {
private boolean isBlocking;
CompletionHandler completionHandler;
@Deprecated
org.glassfish.grizzly.asyncqueue.PushBackHandler pushBackHandler;
MessageCloner cloner;
public void configureBlocking(boolean isBlocking) {
this.isBlocking = isBlocking;
}
public boolean isBlocking() {
return isBlocking;
}
public CompletionHandler getCompletionHandler() {
return completionHandler;
}
public void setCompletionHandler(CompletionHandler completionHandler) {
this.completionHandler = completionHandler;
}
@Deprecated
public org.glassfish.grizzly.asyncqueue.PushBackHandler getPushBackHandler() {
return pushBackHandler;
}
@Deprecated
public void setPushBackHandler(org.glassfish.grizzly.asyncqueue.PushBackHandler pushBackHandler) {
this.pushBackHandler = pushBackHandler;
}
public MessageCloner getMessageCloner() {
return cloner;
}
public void setMessageCloner(final MessageCloner cloner) {
this.cloner = cloner;
}
void reset() {
isBlocking = false;
completionHandler = null;
pushBackHandler = null;
cloner = null;
}
}
static void notifyComplete(
final FilterChainContext context,
final List<CompletionListener> completionListeners) {
final int size = completionListeners.size();
for (int i = size - 1; i >= 0; i--) {
completionListeners.get(i).onComplete(context);
}
completionListeners.clear();
}
static void notifyCopy(
final FilterChainContext srcContext,
final FilterChainContext copiedContext,
final List<CopyListener> copyListeners) {
final int size = copyListeners.size();
for (int i = 0; i < size; i++) {
copyListeners.get(i).onCopy(srcContext, copiedContext);
}
}
/**
* The interface, which represents a listener, which will be notified,
* once {@link FilterChainContext} processing is complete.
*
* @see #addCompletionListener(org.glassfish.grizzly.filterchain.FilterChainContext.CompletionListener)
*/
public interface CompletionListener {
/**
* The method is called, when passed {@link FilterChainContext} processing
* is complete.
*
* @param context
*/
public void onComplete(FilterChainContext context);
}
/**
* The interface, which represents a listener, which will be notified,
* after {@link FilterChainContext#copy()} is called.
*
* @see #addCopyListener(org.glassfish.grizzly.filterchain.FilterChainContext.CopyListener)
*/
public interface CopyListener {
/**
* The method is called, when passed {@link FilterChainContext}
* is copied.
*
* @param srcContext source Context
* @param copiedContext copied Context
*/
public void onCopy(FilterChainContext srcContext,
FilterChainContext copiedContext);
}
}