/* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2007-2008 Sun Microsystems, Inc. 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.html * or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt. * Sun designates this particular file as subject to the "Classpath" exception * as provided by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the License * Header, with the fields enclosed by brackets [] replaced by your own * identifying information: "Portions Copyrighted [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.filter; import com.sun.grizzly.Context; import com.sun.grizzly.Context.AttributeScope; import com.sun.grizzly.ProtocolFilter; import com.sun.grizzly.ProtocolParser; import com.sun.grizzly.util.AttributeHolder; import com.sun.grizzly.util.WorkerThread; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import com.sun.grizzly.util.ThreadAttachment; import com.sun.grizzly.util.ThreadAttachment.Mode; /** * Simple ProtocolFilter implementation which read the available bytes * and delegate the decision of reading more bytes or not to a ProtocolParser. * The ProtocolParser will decide if more bytes are required before continuing * the invokation of the ProtocolChain. * * @author Jeanfrancois Arcand */ public abstract class ParserProtocolFilter extends ReadFilter{ /** * Should ParserProtocolFilter read the data from channel */ protected boolean isSkipRead; public ParserProtocolFilter() { } /** * Read available bytes and delegate the processing of them to the next * ProtocolFilter in the ProtocolChain. * @return true if the next ProtocolFilter on the ProtocolChain * need to be invoked. */ @Override public boolean execute(Context ctx) throws IOException { ProtocolParser parser = null; // Remove Parser from Connection related attributes (if it was there) AttributeHolder connectionAttrs = ctx.getAttributeHolderByScope(AttributeScope.CONNECTION); if (connectionAttrs != null) { parser = (ProtocolParser) connectionAttrs.removeAttribute(ProtocolParser.PARSER); } if (parser == null) { parser = (ProtocolParser) ctx.getAttribute(ProtocolParser.PARSER); if (parser == null) { parser = newProtocolParser(); ctx.setAttribute(ProtocolParser.PARSER, parser); } } else { ctx.setAttribute(ProtocolParser.PARSER, parser); } if (!parser.hasMoreBytesToParse() || parser.isExpectingMoreData()) { if (parser.isExpectingMoreData()) { // Saved ByteBuffer was restored. May be we will not need it next time ((WorkerThread) Thread.currentThread()).updateAttachment(Mode.ATTRIBUTES_ONLY); } boolean continueExecution = isSkipRead || super.execute(ctx); WorkerThread workerThread = (WorkerThread)Thread.currentThread(); ByteBuffer byteBuffer = workerThread.getByteBuffer(); parser.startBuffer(byteBuffer); if (!continueExecution){ return continueExecution; } } if (!parser.hasNextMessage()) { return false; } return invokeProtocolParser(ctx, parser); } /** * Invoke the {@link ProtocolParser}. If more bytes are required, * register the {@link SelectionKey} back to its associated * {@link SelectorHandler} * @param ctx the Context object. * @return true if no more bytes are needed. */ protected boolean invokeProtocolParser(Context ctx, ProtocolParser parser) { if (parser == null){ throw new IllegalStateException("ProcotolParser cannot be null"); } Object o = parser.getNextMessage(); ctx.setAttribute(ProtocolParser.MESSAGE, o); return true; } @Override public boolean postExecute(Context context) throws IOException { ProtocolParser parser = (ProtocolParser) context.getAttribute(ProtocolParser.PARSER); if (parser == null) return true; if (parser != null && parser.hasMoreBytesToParse()) { // Need to say that we read successfully since bytes are left context.setAttribute(ProtocolFilter.SUCCESSFUL_READ, Boolean.TRUE); return true; } SelectionKey key = context.getSelectionKey(); if (parser.isExpectingMoreData()) { if (parser.releaseBuffer()) { saveParser(key, parser); } saveByteBuffer(key); // Register to get more bytes. context.getSelectorHandler().register(key,SelectionKey.OP_READ); return false; } if (parser.releaseBuffer()) { saveParser(key, parser); } return super.postExecute(context); } /** * Return a new or cached ProtocolParser instance. */ public abstract ProtocolParser newProtocolParser(); private void saveByteBuffer(SelectionKey key) { WorkerThread workerThread = (WorkerThread) Thread.currentThread(); // Detach the current Thread data. ThreadAttachment threadAttachment = workerThread.updateAttachment(Mode.BYTE_BUFFER); // Attach it to the SelectionKey so the it can be resumed latter. key.attach(threadAttachment); } private void saveParser(SelectionKey key, ProtocolParser parser) { WorkerThread workerThread = (WorkerThread) Thread.currentThread(); // Detach the current Thread data. ThreadAttachment threadAttachment = workerThread.getAttachment(); threadAttachment.setAttribute(ProtocolParser.PARSER, parser); // Attach it to the SelectionKey so the it can be resumed latter. key.attach(threadAttachment); } /** * Method returns true, if this Filter perform channel read operation on * execute, or false - Filter assumes the data was read by previous Filters in * a chain. * * @return true, if Filter will perform channel read operation on execute. * False - Filter assumes the data was read by previous Filters in a chain. */ protected boolean isSkipRead() { return isSkipRead; } /** * Method set if this Filter should perform channel read operation on * execute, or should assumes the data was read by previous Filters in * a chain. * * @param isSkipRead If isSkipRead is true, this Filter will perform * channel read operation on execute. If false - Filter assumes the data * was read by previous Filters in a chain. */ protected void setSkipRead(boolean isSkipRead) { this.isSkipRead = isSkipRead; } }