/*
 * Copyright (c) 2008, 2017 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.grizzly;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.logging.Logger;
import org.glassfish.grizzly.asyncqueue.MessageCloner;
import org.glassfish.grizzly.attributes.AttributeHolder;
import org.glassfish.grizzly.attributes.AttributeStorage;

Object, which is responsible for holding context during I/O event processing.
Author:Alexey Stashok
/** * Object, which is responsible for holding context during I/O event processing. * * @author Alexey Stashok */
@SuppressWarnings("deprecation") public class Context implements AttributeStorage, Cacheable { private static final Logger LOGGER = Grizzly.logger(Context.class); private static final Processor NULL_PROCESSOR = new NullProcessor(); private static final ThreadCache.CachedTypeIndex<Context> CACHE_IDX = ThreadCache.obtainIndex(Context.class, 4); public static Context create(final Connection connection) { Context context = ThreadCache.takeFromCache(CACHE_IDX); if (context == null) { context = new Context(); } context.setConnection(connection); return context; } public static Context create(final Connection connection, final Processor processor, final IOEvent ioEvent, final IOEventLifeCycleListener lifeCycleListener) { final Context context; if (processor != null) { context = processor.obtainContext(connection); } else { context = NULL_PROCESSOR.obtainContext(connection); } context.setIoEvent(ioEvent); if (lifeCycleListener != null) { context.addLifeCycleListener(lifeCycleListener); } return context; }
Processing Connection
/** * Processing Connection */
private Connection connection;
Processing IOEvent
/** * Processing IOEvent */
protected IOEvent ioEvent = IOEvent.NONE;
Processor, responsible for I/O event processing
/** * Processor, responsible for I/O event processing */
private Processor processor;
Attributes, associated with the processing Context
/** * Attributes, associated with the processing Context */
private final AttributeHolder attributes;
IOEventProcessingHandler is called to notify about IOEvent processing life-cycle events like suspend, resume, complete.
/** * IOEventProcessingHandler is called to notify about IOEvent processing * life-cycle events like suspend, resume, complete. */
protected final MinimalisticArrayList<IOEventLifeCycleListener> lifeCycleListeners = new MinimalisticArrayList<IOEventLifeCycleListener>(IOEventLifeCycleListener.class, 2);
true if this IOEvent processing was suspended during its processing, or false otherwise.
/** * <tt>true</tt> if this IOEvent processing was suspended during its processing, * or <tt>false</tt> otherwise. */
protected boolean wasSuspended; protected boolean isManualIOEventControl; public Context() { attributes = Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createUnsafeAttributeHolder(); }
Notify Context its processing will be suspended in the current thread.
/** * Notify Context its processing will be suspended in the current thread. */
public void suspend() { try { final int sz = lifeCycleListeners.size; final IOEventLifeCycleListener[] array = lifeCycleListeners.array; for (int i = 0; i < sz; i++) { array[i].onContextSuspend(this); } } catch (IOException e) { throw new IllegalStateException(e); } wasSuspended = true; }
Notify Context its processing will be resumed in the current thread.
/** * Notify Context its processing will be resumed in the current thread. */
public void resume() { try { final int sz = lifeCycleListeners.size; final IOEventLifeCycleListener[] array = lifeCycleListeners.array; for (int i = 0; i < sz; i++) { array[i].onContextResume(this); } } catch (IOException e) { throw new IllegalStateException(e); } } public void complete(final ProcessorResult result) { ProcessorExecutor.complete(this, result); }
Returns: true if this IOEvent processing was suspended during its processing, or false otherwise.
/** * @return <tt>true</tt> if this IOEvent processing was suspended during * its processing, or <tt>false</tt> otherwise. */
public boolean wasSuspended() { return wasSuspended; }
Switches processing to the manual IOEvent control. Connection.enableIOEvent(IOEvent) or Connection.disableIOEvent(IOEvent) might be explicitly called.
/** * Switches processing to the manual IOEvent control. * {@link Connection#enableIOEvent(org.glassfish.grizzly.IOEvent)} or * {@link Connection#disableIOEvent(org.glassfish.grizzly.IOEvent)} might be * explicitly called. */
public void setManualIOEventControl() { try { final int sz = lifeCycleListeners.size; final IOEventLifeCycleListener[] array = lifeCycleListeners.array; for (int i = 0; i < sz; i++) { array[i].onContextManualIOEventControl(this); } } catch (IOException e) { throw new IllegalStateException(e); } isManualIOEventControl = true; }
Returns:true, if processing was switched to the manual IOEvent control, or false otherwise.
/** * @return <tt>true</tt>, if processing was switched to the manual IOEvent * control, or <tt>false</tt> otherwise. */
public boolean isManualIOEventControl() { return isManualIOEventControl; }
Get the processing IOEvent.
Returns:the processing IOEvent.
/** * Get the processing {@link IOEvent}. * * @return the processing {@link IOEvent}. */
public IOEvent getIoEvent() { return ioEvent; }
Set the processing IOEvent.
Params:
  • ioEvent – the processing IOEvent.
/** * Set the processing {@link IOEvent}. * * @param ioEvent the processing {@link IOEvent}. */
public void setIoEvent(final IOEvent ioEvent) { this.ioEvent = ioEvent; }
Get the processing Connection.
Returns:the processing Connection.
/** * Get the processing {@link Connection}. * * @return the processing {@link Connection}. */
public Connection getConnection() { return connection; }
Set the processing Connection.
Params:
/** * Set the processing {@link Connection}. * * @param connection the processing {@link Connection}. */
public void setConnection(final Connection connection) { this.connection = connection; }
Get the Processor, which is responsible to process the IOEvent.
Returns:the Processor, which is responsible to process the IOEvent.
/** * Get the {@link Processor}, which is responsible to process * the {@link IOEvent}. * * @return the {@link Processor}, which is responsible to process * the {@link IOEvent}. */
public Processor getProcessor() { return processor; }
Set the Processor, which is responsible to process the IOEvent.
Params:
/** * Set the {@link Processor}, which is responsible to process * the {@link IOEvent}. * * @param processor the {@link Processor}, which is responsible to process * the {@link IOEvent}. */
public void setProcessor(final Processor processor) { this.processor = processor; } public boolean hasLifeCycleListener(final IOEventLifeCycleListener listener) { return lifeCycleListeners.contains(listener); } public void addLifeCycleListener(final IOEventLifeCycleListener listener) { lifeCycleListeners.add(listener); } public boolean removeLifeCycleListener(final IOEventLifeCycleListener listener) { return lifeCycleListeners.remove(listener); } public void removeAllLifeCycleListeners() { lifeCycleListeners.clear(); }
Get attributes (AttributeHolder), associated with the processing Context. AttributeHolder is cleared after each I/O event processing. Method may return null, if there were no attributes added before.
Returns:attributes (AttributeHolder), associated with the processing Context.
/** * Get attributes ({@link AttributeHolder}), associated with the processing * {@link Context}. {@link AttributeHolder} is cleared after each I/O event * processing. * Method may return <tt>null</tt>, if there were no attributes added before. * * @return attributes ({@link AttributeHolder}), associated with the processing * {@link Context}. */
@Override public AttributeHolder getAttributes() { return attributes; }
If implementation uses ObjectPool to store and reuse Context instances - this method will be called before Context will be offered to pool.
/** * If implementation uses {@link org.glassfish.grizzly.utils.ObjectPool} * to store and reuse {@link Context} instances - this method will be * called before {@link Context} will be offered to pool. */
public void reset() { attributes.recycle(); processor = null; lifeCycleListeners.clear(); connection = null; ioEvent = IOEvent.NONE; wasSuspended = false; isManualIOEventControl = false; }
Recycle this Context
/** * Recycle this {@link Context} */
@Override public void recycle() { reset(); ThreadCache.putToCache(CACHE_IDX, this); }
Alternative to recycle() that allows cleanup actions to be performed without returning this instance to the cache. By default, this is a no-op.
Since:2.3.29
/** * Alternative to {@link #recycle()} that allows cleanup actions to be * performed without returning this instance to the cache. * * By default, this is a no-op. * * @since 2.3.29 */
protected void release() { // no-op } private final static class NullProcessor implements Processor { @Override public Context obtainContext(Connection connection) { final Context context = Context.create(connection); context.setProcessor(this); return context; } @Override public ProcessorResult process(Context context) { return ProcessorResult.createNotRun(); } @Override public void read(Connection connection, CompletionHandler completionHandler) { throw new UnsupportedOperationException("Not supported."); } @Override public void write(Connection connection, Object dstAddress, Object message, CompletionHandler completionHandler) { throw new UnsupportedOperationException("Not supported."); } @Override public void write(Connection connection, Object dstAddress, Object message, CompletionHandler completionHandler, MessageCloner messageCloner) { throw new UnsupportedOperationException("Not supported yet."); } @Override @Deprecated public void write(Connection connection, Object dstAddress, Object message, CompletionHandler completionHandler, org.glassfish.grizzly.asyncqueue.PushBackHandler pushBackHandler) { throw new UnsupportedOperationException("Not supported."); } @Override public boolean isInterested(IOEvent ioEvent) { return true; } @Override public void setInterested(IOEvent ioEvent, boolean isInterested) { } } protected static final class MinimalisticArrayList<E> { private E[] array; private int size; @SuppressWarnings("unchecked") private MinimalisticArrayList(final Class<E> clazz, final int initialCapacity) { array = (E[]) Array.newInstance(clazz, initialCapacity); } public void add(final E listener) { ensureCapacity(); array[size++] = listener; } public boolean contains(final E listener) { return indexOf(listener) != -1; } private boolean remove(final E listener) { final int idx = indexOf(listener); if (idx == -1) { return false; } if (idx < size - 1) { System.arraycopy(array, idx + 1, array, idx, size - idx - 1); } array[--size] = null; return true; } public void copyFrom(final MinimalisticArrayList<E> list) { if (list.size > array.length) { array = Arrays.copyOf(list.array, list.size); size = list.size; } else { System.arraycopy(list.array, 0, array, 0, list.size); for (int i = list.size; i < size; i++) { array[i] = null; } size = list.size; } } public int size() { return size; } public E[] array() { return array; } public void clear() { for (int i = 0; i < size; i++) { array[i] = null; } size = 0; } private int indexOf(final E listener) { for (int i = 0; i < size; i++) { if (array[i].equals(listener)) { return i; } } return -1; } private void ensureCapacity() { if (size == array.length) { array = Arrays.copyOf(array, size + 2); } } } }