/*
*
* 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;
}
}