/* 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.playorm.nio.impl.libs; import java.io.IOException; import java.nio.ByteBuffer; import java.util.logging.Level; import java.util.logging.Logger; import org.playorm.nio.api.deprecated.ChannelServiceFactory; import org.playorm.nio.api.deprecated.CorruptPacketException; import org.playorm.nio.api.libs.BufferHelper; import org.playorm.nio.api.libs.PacketListener; import org.playorm.nio.api.libs.PacketProcessor; public class HeaderTrailerProcessor implements PacketProcessor { //-------------------------------------------------------------------- // FIELDS/MEMBERS //-------------------------------------------------------------------- private static final Logger log = Logger.getLogger(HeaderTrailerProcessor.class.getName()); private static final BufferHelper HELPER = ChannelServiceFactory.bufferHelper(null); private PacketListener listener; private int maxSizeContents = 1000000; private byte[] packetSeparator; private ByteBuffer head; private ByteBuffer body; private ByteBuffer tail; private static final int HEADER_SIZE = Integer.SIZE/8; private ProcessingState state = ProcessingState.PROCESSING_HEADER; private Object id; //-------------------------------------------------------------------- // CONSTRUCTORS //-------------------------------------------------------------------- public HeaderTrailerProcessor(Object id, byte[] packetSeparator) { this.id = id; this.packetSeparator = packetSeparator; head = ByteBuffer.allocate(HEADER_SIZE); tail = ByteBuffer.allocate(packetSeparator.length); clearState(); } //-------------------------------------------------------------------- // BUSINESS METHODS //-------------------------------------------------------------------- //-------------------------------------------------------------------- // JAVABEANS GET/SET METHODS //-------------------------------------------------------------------- /** * FILL IN JAVADOC HERE */ public void setPacketListener(PacketListener l) { listener = l; } public ByteBuffer processOutgoing(ByteBuffer b) { if(b.remaining() > maxSizeContents) throw new IllegalArgumentException(id+"Cannot write out packets larger than size=" +maxSizeContents+" actual size11="+b.remaining()); ByteBuffer readOnlyTail = ByteBuffer.wrap(packetSeparator).asReadOnlyBuffer(); ByteBuffer packet = ByteBuffer.allocate(b.remaining()+readOnlyTail.remaining()+4); int size = b.remaining(); 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.log(Level.WARNING, id+"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.log(Level.WARNING, "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.isLoggable(Level.FINEST)) log.log(Level.FINEST, id+"processing stream"); if(b == null) throw new IllegalArgumentException(id+" 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 = HELPER.processForPacket(b, head); if(isDone) { //get header data.... HELPER.doneFillingBuffer(head); int numNeededBytes = head.getInt(); if(numNeededBytes <= 0 || numNeededBytes > maxSizeContents) throw new CorruptPacketException(id+"header='"+numNeededBytes+"' is not valid. Must\n" +"be less than maxSizeContents11=" +maxSizeContents+" and greater than 0", true, false); body = ByteBuffer.allocate(numNeededBytes); body.limit(numNeededBytes); state = ProcessingState.PROCESSING_BODY; head.clear(); } } private void processBody(ByteBuffer b) { if(HELPER.processForPacket(b, body)) { state = ProcessingState.PROCESSING_TAIL; } } private boolean processTail(ByteBuffer b, Object passthrough) throws IOException { if(HELPER.processForPacket(b, tail)) { state = ProcessingState.PROCESSING_HEADER; try { firePacket(passthrough); } finally { clearState(); } return true; } return false; } private void findNewTrailer(ByteBuffer b) { throw new UnsupportedOperationException(id+"recovery is not implemented yet but would be easy to do so"); } private void clearState() { //reset all state first.... state = ProcessingState.PROCESSING_HEADER; HELPER.eraseBuffer(head); HELPER.eraseBuffer(tail); body = null; } private void firePacket(Object passthrough) throws IOException { HELPER.doneFillingBuffer(body); listener.incomingPacket(body, 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 //-------------------------------------------------------------------- }