/* * JLibs: Common Utilities for Java * Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. */ package jlibs.nio.http.filters; import jlibs.nbp.Feeder; import jlibs.nio.Input; import jlibs.nio.Reactor; import jlibs.nio.http.Exchange; import jlibs.nio.http.SocketPayload; import jlibs.nio.http.msg.Message; import jlibs.nio.http.util.MediaType; import jlibs.nio.listeners.IOListener; import jlibs.nio.listeners.Task; import jlibs.nio.util.BufferAllocator; import jlibs.nio.util.Buffers; import jlibs.nio.util.UnpooledBufferAllocator; import jlibs.xml.sax.async.AsyncXMLReader; import jlibs.xml.sax.async.ChannelInputSource; import org.xml.sax.InputSource; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import static java.nio.channels.SelectionKey.OP_READ; /** * @author Santhosh Kumar Tekuri */ public class ParseXML extends ParseSocketPayload{ @Override protected boolean isCompatible(MediaType mt){ return mt.isXML(); } @Override protected boolean parse(Exchange exchange, Message msg, SocketPayload payload, MediaType mt) throws Exception{ String charset = mt.getCharset(null); InputSource is; Input socket = payload.socket(); if(socket!=null && socket.isOpen()){ ReadableByteChannel channel = socket; boolean retain = retain(payload); if(retain){ if(payload.buffers==null) payload.buffers = new Buffers(); } if(payload.buffers!=null) channel = new Reader(payload.buffers, retain, channel); is = new ChannelInputSource(channel); }else is = new InputSource(payload.buffers.new Input()); // todo optimize is.setEncoding(charset); new IOListener().start(new XMLFeedTask(exchange, msg, is), socket, null); return false; } protected boolean retain(SocketPayload payload){ return payload.retain; } protected void addHandlers(AsyncXMLReader xmlReader) throws Exception{} protected void parsingCompleted(Exchange exchange, Message msg, AsyncXMLReader xmlReader){ exchange.resume(); } private class XMLFeedTask extends Task{ private Exchange exchange; private Message msg; private AsyncXMLReader xmlReader; private Feeder feeder; private XMLFeedTask(Exchange exchange, Message msg, InputSource is) throws Exception{ super(OP_READ); this.exchange = exchange; this.msg = msg; xmlReader = new AsyncXMLReader(); addHandlers(xmlReader); feeder = xmlReader.createFeeder(is); } @Override protected boolean process(int readyOp) throws IOException{ feeder = feeder.feed(); if(feeder==null) return true; else{ in.addReadInterest(); return false; } } @Override protected void cleanup(Throwable thr){ if(thr==null) parsingCompleted(exchange, msg, xmlReader); else exchange.resume(thr); } } private static class Reader implements ReadableByteChannel{ private Buffers buffers; private BufferAllocator allocator; private ReadableByteChannel channel; private Buffers backup; private Reader(Buffers buffers, boolean retain, ReadableByteChannel channel){ this.channel = channel; if(buffers.hasRemaining()){ if(retain){ this.buffers = buffers.copy(); allocator = UnpooledBufferAllocator.HEAP; }else{ this.buffers = buffers; allocator = Reactor.current().allocator; } } if(retain) backup = buffers; } @Override public int read(ByteBuffer dst) throws IOException{ if(buffers!=null){ int read = buffers.read(dst, allocator); if(buffers.length==0) buffers = null; return read; } int dstPos = dst.position(); int read = channel.read(dst); if(read>0 && backup!=null){ int dstLimit = dst.limit(); dst.position(dstPos); dst.limit(dstPos+read); backup.write(dst); dst.limit(dstLimit); } return read; } @Override public boolean isOpen(){ return channel.isOpen(); } @Override public void close() throws IOException{ channel.close(); } } }