/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2008-2010 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 com.sun.grizzly.aio; import com.sun.grizzly.Context; import com.sun.grizzly.ContextTask; import com.sun.grizzly.Controller; import com.sun.grizzly.IOEvent; import com.sun.grizzly.ProtocolChain; import com.sun.grizzly.ProtocolChainInstanceHandler; import com.sun.grizzly.ProtocolFilter; import com.sun.grizzly.SelectorHandler; import com.sun.grizzly.async.AsyncQueueReader; import com.sun.grizzly.async.AsyncQueueWritable; import com.sun.grizzly.util.AttributeHolder; import com.sun.grizzly.util.Copyable; import com.sun.grizzly.util.WorkerThread; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.nio.channels.SelectionKey; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; /** * * @author Jeanfrancois Arcand */ public class AIOContext implements Context, CompletionHandler<Integer, Void> { /** * The {@link ProtocolChain} used to execute this {@link Context} */ private ProtocolChain protocolChain; /** * Used to share object between {@link ProtocolFilter}. * WARNING: Attributes which are added are never removed automatically * The removal operation must be done explicitly inside a {@link ProtocolFilter}. */ private Map<String,Object> attributes = null; /** * The {@link Controller} associated with this Context. */ private AIOController controller; /** * The current {@linl ExecutorService} that execute this object. */ private ExecutorService threadPool; /** * An optional {@link IOEvent} that can be invoked * before the {@link ProtocolChain} is invoked. */ private IOEvent<Context> ioEvent; /** * The {@link AsynchronousSocketChannel} used to read/write bytes. */ private AsynchronousSocketChannel channel; /** * Is this context suspended. */ private boolean isSuspended = false; /** * Reference Counter indicating how many Threads share this Context. * Starts at one already counting {@link WorkerThread}. */ private AtomicInteger refCounter=new AtomicInteger(1); private AIOHandler aioHandler; private boolean keepAlive = true; private ByteBuffer byteBuffer; /** * Constructor */ public AIOContext() { } public void copyTo(Copyable copy) { AIOContext copyContext = (AIOContext) copy; copyContext.protocolChain = protocolChain; if (attributes != null) { copyContext.attributes = new HashMap<String, Object>(attributes); } copyContext.controller = controller; copyContext.threadPool = threadPool; copyContext.ioEvent = ioEvent; } /** * Remove a key/value object. * @param key - name of an attribute * @return attribute which has been removed */ public Object removeAttribute(String key){ if (attributes == null){ return null; } return attributes.remove(key); } /** * Set a key/value object. * @param key - name of an attribute * @param value - value of named attribute */ public void setAttribute(String key,Object value){ if (attributes == null){ attributes = new HashMap<String,Object>(); } attributes.put(key,value); } /** * Return an object based on a key. * @param key - name of an attribute * @return - attribute value for the <tt>key</tt>, null if <tt>key</tt> * does not exist in <tt>attributes</tt> */ public Object getAttribute(String key){ if (attributes == null){ return null; } return attributes.get(key); } /** * Return {@link AttributeHolder}, which corresponds to the * given {@link AttributeScope}> * * @param scope - {@link AttributeScope}> * @return - {@link AttributeHolder} instance, which contains * {@link AttributeScope}> attributes */ public AttributeHolder getAttributeHolderByScope(AttributeScope scope) { AttributeHolder holder = null; switch (scope) { case REQUEST: holder = this; break; case CONNECTION: Object attachment = getSelectionKey().attachment(); if (attachment instanceof AttributeHolder) { holder = (AttributeHolder) attachment; } break; case SELECTOR: holder = aioHandler; break; case CONTROLLER: holder = controller; break; } return holder; } /** * Set a {@link Map} of attribute name/value pairs. * Old {@link AttributeHolder} values will not be available. * Later changes of this {@link Map} will lead to changes to the current * {@link AttributeHolder}. * * @param attributes - map of name/value pairs */ public void setAttributes(Map<String, Object> attributes) { this.attributes = attributes; } /** * Return a {@link Map} of attribute name/value pairs. * Updates, performed on the returned {@link Map} will be reflected in * this {@link AttributeHolder} * * @return - {@link Map} of attribute name/value pairs */ public Map<String, Object> getAttributes() { return attributes; } /** * Return the current {@link SelectionKey}. * @return - this Context's SelectionKey */ public SelectionKey getSelectionKey() { return null; } /** * Return the current {@link Controller}. * @return - this Context's current {@link Controller} */ public AIOController getController() { return controller; } /** * Set the current {@link Controller}. * @param {@link Controller} */ public void setController(AIOController controller) { this.controller = controller; } /** * Recycle this instance. */ public void recycle() { if (isSuspended) { throw new IllegalStateException("The Context has been marked as " + "suspended and cannot be recycled"); } ioEvent = null; if (attributes != null) { attributes.clear(); } isSuspended = false; refCounter.set(1); getProtocolChainInstanceHandler().offer(protocolChain); protocolChain = null; } /** * Return {@link SelectionKey}'s next registration state. * @return this Context's SelectionKey registration state */ public KeyRegistrationState getKeyRegistrationState() { return null; } /** * Set the {@link SelectionKey}'s next registration state * @param {@link keyRegistrationState} - set this Context's SelectionKey * registration state */ public void setKeyRegistrationState(KeyRegistrationState keyRegistrationState) { } /** * Return {@link ProtocolChain} executed by this instance. * @return {@link ProtocolChain} instance */ public ProtocolChain getProtocolChain() { return protocolChain; } /** * Set the {@link ProtocolChain} used by this {@link Context}. * @param protocolChain instance of {@link ProtocolChain} to be used by the Context */ public void setProtocolChain(ProtocolChain protocolChain) { this.protocolChain = protocolChain; } /** * Get the current {@link SelectionKey} interest ops this instance is executing. * @return OpType the currentOpType. */ public OpType getCurrentOpType() { return null; } /** * Set the current OpType value. * @param currentOpType sets current operation type */ public void setCurrentOpType(OpType currentOpType) { } /** * Execute this Context using the Controller's thread pool * @deprecated */ public void execute() { } /** * Execute this Context using the Controller's thread pool * @param contextTask {@link ContextTask}, which will be * executed by {@link ExecutorService} */ public void execute(ContextTask contextTask) { execute(contextTask, true); } /** * Execute this Context using either Controller's thread pool or current thread * @param contextTask {@link ContextTask}, which will be * executed by {@link ExecutorService} * @param runInSeparateThread if true - {@link ContextTask} will * be executed in separate thread, false - in current thread. */ @SuppressWarnings("unchecked") public void execute(ContextTask contextTask, boolean runInSeparateThread) { if (protocolChain == null) { ProtocolChainInstanceHandler pciHandler = getProtocolChainInstanceHandler(); protocolChain = pciHandler.poll(); } if (contextTask != null) { contextTask.setContext(this); try { contextTask.call(); } catch (Exception e) { AIOController.logger().log(Level.SEVERE, "Unexpected exception occured, when executing task: " + contextTask, e); } } } /** * Return the {@link ProtocolChainInstanceListener} associated with this * {@link Context} * @return ProtocolChainInstanceListener */ public ProtocolChainInstanceHandler getProtocolChainInstanceHandler() { ProtocolChainInstanceHandler protocolChainInstanceHandler = aioHandler.getProtocolChainInstanceHandler(); return protocolChainInstanceHandler != null ? protocolChainInstanceHandler : controller.getProtocolChainInstanceHandler(); } /** * Return the {@link ExecutorService} executing this instance. * @return {@link ExecutorService} */ public ExecutorService getThreadPool() { if (threadPool == null && controller != null){ threadPool = controller.getThreadPool(); } return threadPool; } /** * Set the {@link ExecutorService} that will execute this instance. * @param threadPool the {@link ExecutorService} to set */ public void setThreadPool(ExecutorService threadPool) { this.threadPool = threadPool; } /** * Set an optional CallbackHandler. * @param ioEvent the {@link IOEvent} to set */ public void setIOEvent(IOEvent<Context> ioEvent){ this.ioEvent = ioEvent; } /** * Return the current {@link IOEvent} associated with this * instance. * @return IOEvent the current {@link IOEvent} associated with this * instance. */ public IOEvent getIOEvent(){ return ioEvent; } /** * Return the current {@link Controller#Protocol} this instance is executing. * @return the current Controller.Protocol this instance is executing. */ public AIOController.Protocol getProtocol() { return aioHandler.protocol(); } /** * @Deprecated * * Set the current {@link Controller#Protocol} this instance is executing. * @param protocol The current protocol. */ public void setProtocol(AIOController.Protocol protocol) { } /** * Return the current {@link SelectorHandler} this instance is executing. * @return the current {@link SelectorHandler} this instance is executing. * @deprecated */ public SelectorHandler getSelectorHandler() { return null; } public AIOHandler getAIOHandler() { return aioHandler; } public void setAIOHandler(AIOHandler aioHandler) { this.aioHandler = aioHandler; } public AsynchronousSocketChannel getChannel() { return channel; } public void setChannel(AsynchronousSocketChannel channel){ this.channel = channel; } public void completed(Integer count, Void attachment) { // Catch closed connection. if (count == -1){ try{ channel.close(); } catch (IOException ex2){ } finally { controller.returnContext(this); } return; } try{ getProtocolChain().execute(this); } catch (Throwable t){ AIOController.logger().log(Level.SEVERE,"ProtocolChain exception",t); } } public void failed(Throwable t, Void attachment) { try{ channel.close(); } catch (IOException ex){ if (AIOController.logger().isLoggable(Level.FINE)){ AIOController.logger().log(Level.FINE,"postExecute()", ex); } } finally{ controller.returnContext(this); } } public void cancelled(Void attachment) { if (AIOController.logger().isLoggable(Level.FINEST)){ AIOController.logger().log(Level.FINEST, "Pending Connection cancelled", attachment); } controller.returnContext(this); } /** * Suspend the execution of this {@link Context}. Suspending the execution * allow application to store the current instance, and re-use it later * by not only the Thread used when called suspend, but also from any other Thread. * A suspended Context will not be re-used by any other transaction and Thread. * A suspended Context will keep its current state intact, meaning its * SelectionKey, attributes, SelectorHandler, etc, will not change. Internally, * The Context will not be recyled and will not be re-used by any Thread. * * When invoked this method will automatically set the * {@link Context#setKeyRegistrationState} to {@link KeyRegistrationState} * to KeyRegistrationState.NONE. * * Invoking this method many times as not effect once suspended. */ public void suspend(){ if (isSuspended) return; isSuspended = true; incrementRefCount(); } /** * Return <tt>true</tt> if this Context has been suspended by * invoking {@link suspend}. When suspended, invoking {@link Context#recycle} * will throw an {@link IllegalStateException} * @return <tt>true</tt> if this Context has been suspended */ public boolean isSuspended(){ return isSuspended; } /** * Resume a {@link #suspend}ed {@link Context}. * <strong>Resume will not call {@link Context#recycle}</strong>. So * after the caller is finished using Context caller must * call {@link Controller#returnContext(com.sun.grizzly.Context)} * to mark it as a candidate for being re-used by another Thread and connection. * * <strong>Important. When resumed, all operations done on this * object are not thread-safe and there is probability that another * thread is already using this object. Never use this object once resumed.</strong> * * When invoked this method will automatically set the * {@link Context#setKeyRegistrationState} to {@link KeyRegistrationState} * to KeyRegistrationState.REGISTER and automatically re-enable read and * write operations. * * If the Context hasn't been suspended, calling that method has no effet. */ public void resume(){ if (!isSuspended) return; isSuspended = false; } /** * Cancel a {@link #suspend}ed {@link Context}. Invoking this method will * automatically clean the state of this Context and mark it as a candidate * for being re-used by another Thread and connection. * * <strong>Important. When cancelled, all operations done on this * object are not thread-safe and there is probability that another * thread is already using this object. Never use this object once cancelled.</strong> * * * When invoked this method will automatically close the underlying * connection (represented by its {@link SelectionKey}. * * If the Context hasn't been suspended, calling that method has no effet. */ public void cancel(){ if (!isSuspended) return; isSuspended = false; getController().returnContext(this); } /** * Called by outer Threads that are not instances of {@link WorkerThread} to * indicate that this {@link Context} should not be * {@link #recycle()} or offered back to its pool. * * When a outer Thread is done with {@link Context} it must call * {@link Controller#returnContext(com.sun.grizzly.Context) to * ensure that {@link Context} will be properly recycled. * * @return Current Thread reference count */ public void incrementRefCount(){ refCounter.incrementAndGet(); } /** * Decrements the reference count of this {@link Context}. * Threads wanting to release {@link Context} should not call * this method but instead use * {@link Controller#returnContext(com.sun.grizzly.Context)} * @return return decremented reference count */ public int decrementRefCount(){ return refCounter.decrementAndGet(); } /** * Returns {@link AsyncQueueWritable}, assciated with the current * {@link Context}. This method is not threadsafe. * * @return {@link AsyncQueueWritable} */ public AsyncQueueWritable getAsyncQueueWritable(){ return null; } /** * Return the {@linkAsyncQueueReader} * @return the {@linkAsyncQueueReader} */ public AsyncQueueReader getAsyncQueueReader(){ return null; } public boolean isKeepAlive() { return keepAlive; } public void setKeepAlive(boolean keepAlive) { this.keepAlive = keepAlive; } public ByteBuffer getByteBuffer() { return byteBuffer; } public void setByteBuffer(ByteBuffer byteBuffer) { this.byteBuffer = byteBuffer; } }