/* Copyright (c) 2002, Dean Hiller All rights reserved. ***************************************************************** IF YOU MAKE CHANGES TO THIS CODE AND DO NOT POST THEM, YOU WILL BE IN VIOLATION OF THE LICENSE I HAVE GIVEN YOU. Contact me at deanhiller@users.sourceforge.net if you need a different license. ***************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.webpieces.nio.test.nottested; import java.io.IOException; import java.nio.ByteBuffer; import java.util.logging.Level; import org.webpieces.util.logging.Logger; import org.webpieces.nio.api.deprecated.ChannelServiceFactory; import org.webpieces.nio.api.deprecated.CorruptPacketException; import org.webpieces.nio.api.libs.BufferHelper; import org.webpieces.nio.api.libs.PacketListener; import org.webpieces.nio.api.libs.PacketProcessor; import org.webpieces.nio.impl.libs.ProcessingState; public class SequenceProcessor implements PacketProcessor { //-------------------------------------------------------------------- // FIELDS/MEMBERS //-------------------------------------------------------------------- private static final Logger log = LoggerFactory.getLogger(SequenceProcessor.class); private static final int HEADER_SIZE = Integer.SIZE/8*2; private static final BufferHelper HELPER = ChannelServiceFactory.bufferHelper(null); private Object id; private PacketListener listener; private int sequenceNum = -1; private ProcessingState state = ProcessingState.PROCESSING_HEADER; private int maxSizeContents; private byte[] packetSeparator; private BufferInfo head; private BufferInfo body; private BufferInfo tail; private class BufferInfo { private boolean haveData = false; private ByteBuffer cache; private int index = 0; private int numNeededBytes = -1; private int sequence; public void setHaveData(boolean haveData) { this.haveData = haveData; } public boolean isHaveData() { return haveData; } public void setCache(ByteBuffer cache) { this.cache = cache; } public ByteBuffer getCache() { return cache; } public void setIndex(int index) { this.index = index; } public int getIndex() { return index; } public void setNumNeededBytes(int numNeededBytes) { this.numNeededBytes = numNeededBytes; } public int getNumNeededBytes() { return numNeededBytes; } public void setSequence(int sequence) { this.sequence = sequence; } public int getSequence() { return sequence; } } //-------------------------------------------------------------------- // CONSTRUCTORS //-------------------------------------------------------------------- public SequenceProcessor(Object id, int maxSizeContents, byte[] packetSeparator) { this.id = id; this.packetSeparator = packetSeparator; this.maxSizeContents = maxSizeContents; head = new BufferInfo(); body = new BufferInfo(); tail = new BufferInfo(); body.setCache(ByteBuffer.allocate(maxSizeContents)); head.setCache(ByteBuffer.allocate(HEADER_SIZE)); tail.setCache(ByteBuffer.allocate(packetSeparator.length)); clearState(); } //-------------------------------------------------------------------- // BUSINESS METHODS //-------------------------------------------------------------------- //-------------------------------------------------------------------- // JAVABEANS GET/SET METHODS //-------------------------------------------------------------------- /** * FILL IN JAVADOC HERE */ public void setPacketListener(PacketListener l) { listener = l; } private synchronized int getSequence() { return sequenceNum++; } public ByteBuffer processOutgoing(ByteBuffer b) { if(b.remaining() > maxSizeContents) throw new IllegalArgumentException("Cannot write out packets larger than size="+maxSizeContents); ByteBuffer readOnlyTail = ByteBuffer.wrap(packetSeparator).asReadOnlyBuffer(); int seq = getSequence(); if(log.isTraceEnabled()) log.trace(id+"packetized-> #"+seq+" buf="+b); ByteBuffer packet = ByteBuffer.allocate(b.remaining()+readOnlyTail.remaining()+HEADER_SIZE); int size = b.remaining(); packet.putInt(seq); packet.putInt(size); packet.put(b); packet.put(readOnlyTail); HELPER.doneFillingBuffer(packet); return packet; } /* (non-Javadoc) * @see org.dhiller.common.bus.Listener#notify(java.lang.Object) */ public boolean incomingData(ByteBuffer b, Object passthrough) throws IOException { try { return notifyImpl(b, passthrough); } catch(CorruptPacketException e) { log.error("Corrupt packet received", e); clearState(); throw e; } } // I think recovering could be a security problem...if they send bad data...kill the link // private void recover(ByteBuffer b) { // try { // throw new UnsupportedOperationException("recovery is not supported yet"); // //state = RECOVERING; // //notifyImpl(b); // } catch(RuntimeException e) { // log.error("Ignoring exception as it happened after another\n" // +"exception that we will throw back. This exception may be\n" // +"a result of the first exception", e); // } // } public boolean notifyImpl(ByteBuffer b, Object passthrough) throws IOException { if (log.isTraceEnabled()) log.log(Level.FINEST, "processing stream"); if(b == null) throw new IllegalArgumentException("evt cannot be null"); boolean notified = false; while(b.remaining() > 0) { switch(state) { case PROCESSING_HEADER: processHeader(b); break; case PROCESSING_BODY: processBody(b); break; case PROCESSING_TAIL: notified = processTail(b, passthrough); break; case RECOVERING: findNewTrailer(b); break; default: break; } } return notified; } private void processHeader(ByteBuffer b) { boolean isDone = processDone(b, head); if(isDone) { //get header data.... HELPER.doneFillingBuffer(head.getCache()); body.setSequence(head.getCache().getInt()); body.setNumNeededBytes(head.getCache().getInt()); state = ProcessingState.PROCESSING_BODY; head.getCache().clear(); if(body.getNumNeededBytes() <= 0 || body.getNumNeededBytes() > maxSizeContents) throw new CorruptPacketException("header='"+body.getNumNeededBytes()+"' is not valid. Must\n" +"be less than maxSizeContents=" +maxSizeContents+" and greater than 0", true, false); } } private void processBody(ByteBuffer b) { if(processDone(b, body)) { state = ProcessingState.PROCESSING_TAIL; } } private boolean processTail(ByteBuffer b, Object passthrough) throws IOException { if(processDone(b, tail)) { state = ProcessingState.PROCESSING_HEADER; try { firePacket(passthrough); } finally { clearState(); } return true; } return false; } private void findNewTrailer(ByteBuffer b) { throw new UnsupportedOperationException("recovery is not implemented yet but would be easy to do so"); } private boolean processDone(ByteBuffer b, BufferInfo info) { int remain = b.remaining(); int cacheSpace = info.getNumNeededBytes(); int sizeToCopy = cacheSpace; if(remain <= 0) return info.isHaveData(); if(remain < cacheSpace) sizeToCopy = remain; byte[] dst = info.getCache().array(); if(sizeToCopy > 0) { info.setNumNeededBytes(info.getNumNeededBytes() - sizeToCopy); b.get(dst, info.getIndex(), sizeToCopy); int curPos = info.getCache().position(); info.getCache().position(curPos+sizeToCopy); info.setIndex(info.getIndex() + sizeToCopy); } if(info.getNumNeededBytes() <= 0) { return true; } return false; } private void clearState() { //reset all state first.... state = ProcessingState.PROCESSING_HEADER; head.setNumNeededBytes(HEADER_SIZE); body.setNumNeededBytes(-1); tail.setNumNeededBytes(packetSeparator.length); head.setIndex(0); body.setIndex(0); tail.setIndex(0); head.setHaveData(false); body.setHaveData(false); tail.setHaveData(false); HELPER.eraseBuffer(head.getCache()); HELPER.eraseBuffer(body.getCache()); HELPER.eraseBuffer(tail.getCache()); } private void firePacket(Object passthrough) throws IOException { HELPER.doneFillingBuffer(body.getCache()); if(log.isTraceEnabled()) log.trace(id+"unpacketize<- #"+body.getSequence()+" b="+body.getCache()); listener.incomingPacket(body.getCache(), passthrough); } //-------------------------------------------------------------------- //Event Handler LIBRARY helpers //These functions are like library functions that the event handlers above //share to get their work done. //-------------------------------------------------------------------- //-------------------------------------------------------------------- // ADD LISTENERS/FIRE EVENT METHODS //-------------------------------------------------------------------- //-------------------------------------------------------------------- // INTERFACES/CLASSES //-------------------------------------------------------------------- }