Author: costin
Date: Sat Nov  5 19:13:06 2005
New Revision: 331062

URL: http://svn.apache.org/viewcvs?rev=331062&view=rev
Log:
Code for testing ( and using ) coyote standalone. 

The adapters are very simple, more as example. The goal is to have a
minimal http connector, and play with nio and other apis in a simpler 
environment

About MessageWriter and MessageReader - they are copies of
Catalina/connector/OutputBuffer and InputBuffer. Renamed to avoid
confusion. Long term, they should be part of o.a.coyote package, there
is no dep on catalina and are usefull in in connector. 


Added:
    tomcat/sandbox/java/org/apache/coyote/
    tomcat/sandbox/java/org/apache/coyote/adapters/
    tomcat/sandbox/java/org/apache/coyote/adapters/Counters.java
    tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java
    tomcat/sandbox/java/org/apache/coyote/adapters/HelloWorldAdapter.java
    tomcat/sandbox/java/org/apache/coyote/adapters/JsAdapter.java
    tomcat/sandbox/java/org/apache/coyote/adapters/Mapper.java
    tomcat/sandbox/java/org/apache/coyote/standalone/
    tomcat/sandbox/java/org/apache/coyote/standalone/ClientAbortException.java
    tomcat/sandbox/java/org/apache/coyote/standalone/Main.java
    tomcat/sandbox/java/org/apache/coyote/standalone/MessageReader.java
    tomcat/sandbox/java/org/apache/coyote/standalone/MessageWriter.java

Added: tomcat/sandbox/java/org/apache/coyote/adapters/Counters.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/adapters/Counters.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/adapters/Counters.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/adapters/Counters.java Sat Nov  5 
19:13:06 2005
@@ -0,0 +1,78 @@
+package org.apache.coyote.adapters;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+/**
+ * Used to collect statistics to evaluate performance of the coyote layer.
+ * 
+ */
+public class Counters implements Adapter {
+    
+    // per thread
+    public static class CountData {
+        public long time;
+        public long requests;
+        public int exceptions;
+    }
+
+    // quick hack - need to move the per-thread code from tomcat
+    List counters=new ArrayList();
+    ThreadLocal tl=new ThreadLocal();
+    
+    Adapter next;
+    
+    public Counters() {
+    }
+    
+    public void setNext( Adapter adapter ) {
+        next=adapter;
+    }
+    
+    public Adapter getNext( ) {
+        return next;
+    }
+
+    public void service(Request req, final Response res) throws Exception {
+        long t0=System.currentTimeMillis();
+        CountData cnt=(CountData)tl.get();
+        if( cnt == null ) {
+            cnt=new CountData();
+            counters.add( cnt );
+            tl.set( cnt );
+            // TODO: deal with thread death
+        }
+        
+        cnt.requests++;
+        try {
+            next.service(req,res);
+        } catch( Exception ex ) {    
+            cnt.exceptions++;
+            throw ex;
+        } finally {
+            long t1=System.currentTimeMillis();            
+            cnt.time+=( t1-t0);            
+        }
+        
+    }
+
+    /** Returns statistics for the server.
+     *  TODO: make it per thread, agregate all threads
+     *  
+     * @return
+     */
+    public CountData getCounts() {
+        CountData total=new CountData();
+        for( int i=0; i< counters.size(); i++ ) {
+            CountData cd=((CountData)counters.get(i));
+            total.requests+= cd.requests;
+            total.time+=cd.time;
+            total.exceptions+=cd.exceptions;
+        }
+        return total;
+    }
+}
\ No newline at end of file

Added: tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java Sat Nov  5 
19:13:06 2005
@@ -0,0 +1,127 @@
+package org.apache.coyote.adapters;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.coyote.standalone.MessageWriter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+
+/**
+ * Serve a static file. This is the traditional method, a separate adapter 
could
+ * use Sendfile.
+ * 
+ * No fancy things. Not sure if it should have dir support even.
+ */
+public class FileAdapter implements Adapter {
+    Log log = LogFactory.getLog("coyote.file");
+
+    private String baseDir = "html/";
+
+    private File baseDirF;
+
+    public FileAdapter() {
+        init();
+    }
+
+    public void setBaseDir(String s) {
+        baseDir = s;
+    }
+
+    public void init() {
+        baseDirF = new File(baseDir);
+        try {
+            baseDir = baseDirF.getCanonicalPath();
+        } catch (IOException e) {
+        }
+    }
+
+    public void service(Request req, final Response res) throws Exception {
+
+        String uri = req.requestURI().toString();
+        if (uri.indexOf("..") >= 0) {
+            // not supported, too dangerous
+            // what else to escape ?
+            log.info("Invalid .. in  " + uri);
+            res.setStatus(404);
+            return;
+        }
+
+        // local file
+        File f = new File(baseDirF, uri);
+
+        // extra check
+        if (!f.getCanonicalPath().startsWith(baseDir)) {
+            log.info("File outside basedir " + baseDir + " " + f);
+            res.setStatus(404);
+            return;
+        }
+
+        if (f.isDirectory()) {
+            // check for index.html, redirect if exists
+            // list dir if not
+
+            f = new File(f, "index.html");
+        }
+
+        if (!f.exists()) {
+            log.info("File not found  " + f);
+            res.setStatus(404);
+            return;
+        }
+
+        res.setStatus(200);
+
+        // TODO: read from a resources in classpath !
+        // TODO: refactor to allow sendfile
+        // TODO: read mime types
+
+        int dot=uri.lastIndexOf(".");
+        if( dot > 0 ) {
+            String ext=uri.substring(dot+1);
+            String ct=getContentType(ext);
+            if( ct!=null) {
+                res.setContentType(ct);
+            }
+        }
+
+        res.setContentLength(f.length());
+
+        res.sendHeaders();
+
+        // not used - writes directly to response
+        // MessageWriter out = MessageWriter.getWriter(req, res, 0);
+
+        FileInputStream fis = new FileInputStream(f);
+        byte b[] = new byte[4096];
+        ByteChunk mb = new ByteChunk();
+        int rd = 0;
+        while ((rd = fis.read(b)) > 0) {
+            mb.setBytes(b, 0, rd);
+            res.doWrite(mb);
+        }
+
+    }
+
+    static Properties contentTypes=new Properties();
+    static {
+        initContentTypes();
+    }
+    static void initContentTypes() {
+        contentTypes.put("html", "text/html");
+        contentTypes.put("txt", "text/plain");
+        contentTypes.put("xul", "application/vnd.mozilla.xul+xml");
+    }
+    
+    public String getContentType( String ext ) {
+        return contentTypes.getProperty( ext, "text/plain" );
+    }
+    
+}
\ No newline at end of file

Added: tomcat/sandbox/java/org/apache/coyote/adapters/HelloWorldAdapter.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/adapters/HelloWorldAdapter.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/adapters/HelloWorldAdapter.java 
(added)
+++ tomcat/sandbox/java/org/apache/coyote/adapters/HelloWorldAdapter.java Sat 
Nov  5 19:13:06 2005
@@ -0,0 +1,15 @@
+package org.apache.coyote.adapters;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.coyote.standalone.MessageWriter;
+
+public class HelloWorldAdapter implements Adapter {
+    public void service(Request req, Response res) throws Exception {
+        MessageWriter out=MessageWriter.getWriter(req, res, 0);
+        res.setContentType("text/html");
+        
+        out.write("<h1>Hello world</h1>");
+    }
+}

Added: tomcat/sandbox/java/org/apache/coyote/adapters/JsAdapter.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/adapters/JsAdapter.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/adapters/JsAdapter.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/adapters/JsAdapter.java Sat Nov  5 
19:13:06 2005
@@ -0,0 +1,240 @@
+package org.apache.coyote.adapters;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.Http11BaseProtocol;
+import org.apache.coyote.standalone.Main;
+import org.apache.coyote.standalone.MessageWriter;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.EvaluatorException;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.WrappedException;
+
+/**
+ * Will load the 'default.js' and execute init. In init you can set params on
+ * the server and handler.
+ *
+ * Entry points:
+ * <ul> 
+ * <li>init() - called the first time, can set the port and other
+ * properties in the "server" object 
+ * <li>initThread() - called per thread. 
+ * <li>service( req, res, out)
+ * </ul>
+ * 
+ * Defined objects:
+ * <ul> 
+ * <li>log - commons logger log for this adapter 
+ * <li>server - the http connector 
+ * <li>jsAdapter, counterAdapter, fileAdapter, mapperAdapter - adapters.
+ * </ul>
+ * 
+ * Note: this is just an example, you can extend it or create your own
+ * with different semantics. After the coyote API is cleaned up and converted
+ * to NIO - I'll also define a better JS API.
+ * 
+ */
+public class JsAdapter extends Main implements Adapter {
+
+    // Js file to interpret
+    static String filename = "js-bin/default.js";
+
+    // to support reloading of the js file
+    static long lastModif = 0;
+
+    // Javascript main context and scope
+    private Context mainCx;
+
+    private Scriptable mainScope;
+
+    private Log log = LogFactory.getLog("js");
+
+    // we store them per thread. Need a way to manage the tokens
+    public static final int SCOPE_NOTE = 12;
+
+    public static final int ADAPTER_NOTES = 11;
+
+    boolean reload = true;
+
+    public JsAdapter() {
+    }
+
+    public boolean getReload() {
+        return reload;
+    }
+
+    public void setReload(boolean reload) {
+        this.reload = reload;
+    }
+
+    public void service(Request req, final Response res) throws Exception {
+        // Per thread.
+        Context cx = (Context) req.getNote(JsAdapter.ADAPTER_NOTES);
+        Scriptable scope = (Scriptable) req.getNote(JsAdapter.SCOPE_NOTE);
+        MessageWriter out = MessageWriter.getWriter(req, res, 0);
+
+        if (cx == null) {
+            cx = Context.enter();
+            req.setNote(JsAdapter.ADAPTER_NOTES, cx);
+            // TODO: exit on thread death
+
+            // Each thread will have an associated context, and an associated
+            // scope.
+            // The scope will hold the proxies for req, res and the other
+            // objects
+            // Because the req and response never change for a thread - we 
don't
+            // need to bind them again.
+
+            scope = cx.newObject(mainScope);
+            scope.setPrototype(mainScope);
+
+            req.setNote(JsAdapter.SCOPE_NOTE, scope);
+
+            Object fObj = mainScope.get("initThread", mainScope);
+            if ((fObj instanceof Function)) {
+                Object functionArgs[] = { req, res, out };
+                Function f = (Function) fObj;
+                Object result = f.call(mainCx, mainScope, mainScope,
+                        functionArgs);
+            }
+        }
+
+        // The file was loaded in initJS(), at server startup. We will only
+        // check if it changed, if we are in devel mode and reload it.
+        if (reload) {
+            load(filename);
+        }
+
+        // Now call the service() js function
+        Object fObj = ScriptableObject.getProperty(scope, "service");
+        if (!(fObj instanceof Function)) {
+            log.info("service is undefined or not a function. " + fObj);
+            log.info("Not found in scope... " + scope);
+            log.info("Parent: " + mainScope.get("service", mainScope));
+            fObj = mainScope.get("service", mainScope);
+        }
+        
+        Object functionArgs[] = { req, res, out };
+        Function f = (Function) fObj;
+        Object result = f.call(cx, scope, scope, functionArgs);
+        
+    }
+
+    /**
+     * Initialize. Will run the init() method in the script
+     * 
+     * @param proto
+     */
+    public void initJS() {
+        mainCx = Context.enter();
+        mainScope = mainCx.initStandardObjects();
+
+        Object jsOut;
+        
+        jsOut = Context.javaToJS(log, mainScope);
+        ScriptableObject.putProperty(mainScope, "log", jsOut);
+
+        jsOut = Context.javaToJS(proto, mainScope);
+        ScriptableObject.putProperty(mainScope, "server", jsOut);
+
+        Counters ct = (Counters) proto.getAdapter();
+        Mapper mp = (Mapper) ct.getNext();
+        FileAdapter fa = (FileAdapter) mp.getDefaultAdapter();
+
+        jsOut = Context.javaToJS(ct, mainScope);
+        ScriptableObject.putProperty(mainScope, "countersAdapter", jsOut);
+
+        jsOut = Context.javaToJS(mp, mainScope);
+        ScriptableObject.putProperty(mainScope, "mapperAdapter", jsOut);
+
+        jsOut = Context.javaToJS(fa, mainScope);
+        ScriptableObject.putProperty(mainScope, "fileAdapter", jsOut);
+
+        jsOut = Context.javaToJS(this, mainScope);
+        ScriptableObject.putProperty(mainScope, "jsAdapter", jsOut);
+
+        load(filename);
+
+        Object fObj = mainScope.get("init", mainScope);
+        if ((fObj instanceof Function)) {
+            Object functionArgs[] = {};
+            Function f = (Function) fObj;
+            Object result = f.call(mainCx, mainScope, mainScope, functionArgs);
+        }
+    }
+
+    /**
+     * Load a script.
+     */
+    private void load(String filename) {
+
+        FileReader in = null;
+        try {
+            File scriptF = new File(filename);
+
+            if (lastModif != 0 && scriptF.lastModified() <= lastModif) {
+                // already loaded
+                return;
+            }
+            lastModif = scriptF.lastModified();
+            in = new FileReader(filename);
+        } catch (FileNotFoundException ex) {
+            System.err.println("JS file not found " + ex);
+            return;
+        }
+
+        try {
+            // Here we evalute the entire contents of the file as
+            // a script. Text is printed only if the print() function
+            // is called.
+            Object result = mainCx.evaluateReader(mainScope, in, filename, 1,
+                    null);
+        } catch (WrappedException we) {
+            System.err.println(we.getWrappedException().toString());
+            we.printStackTrace();
+        } catch (EvaluatorException ee) {
+            System.err.println("js: " + ee.getMessage());
+        } catch (JavaScriptException jse) {
+            System.err.println("js: " + jse.getMessage());
+        } catch (IOException ioe) {
+            System.err.println(ioe.toString());
+        } finally {
+            try {
+                in.close();
+            } catch (IOException ioe) {
+                System.err.println(ioe.toString());
+            }
+        }
+    }
+
+    public void setProtocol(Http11BaseProtocol proto) {
+        this.proto = proto;
+    }
+
+    // ------------ Example on how to run it --------------------
+
+    public void init() {
+        super.init();
+        JsAdapter js = this; // new JsAdapter();
+        js.setProtocol(proto);
+        js.initJS();
+        mainAdapter.addAdapter("/js-bin", js);
+    }
+
+    public static void main(String args[]) {
+        JsAdapter sa = new JsAdapter();
+        sa.run(); // in Main - will call init first
+    }
+
+}
\ No newline at end of file

Added: tomcat/sandbox/java/org/apache/coyote/adapters/Mapper.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/adapters/Mapper.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/adapters/Mapper.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/adapters/Mapper.java Sat Nov  5 
19:13:06 2005
@@ -0,0 +1,125 @@
+package org.apache.coyote.adapters;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.Http11BaseProtocol;
+import org.apache.coyote.standalone.MessageWriter;
+import org.apache.tomcat.util.loader.Loader;
+import org.apache.tomcat.util.loader.Repository;
+
+/**
+ * Very, very simple mapper for standalone coyote. Used to test and experiment
+ * various low level changes, or for very simple http servers.
+ * 
+ * It currently supports only prefix mapping, using the first url component.
+ */
+public class Mapper implements Adapter {
+    
+    // TODO: add extension mappings 
+    // Key = prefix, one level only, value= class name of Adapter
+    // key starts with a / and has no other / ( /foo - but not /foo/bar )
+    Hashtable prefixMap=new Hashtable();
+
+    String fileAdapterCN="org.apache.coyote.adapters.FileAdapter";
+    Adapter defaultAdapter=new FileAdapter();    
+
+    public Mapper() {
+    }
+
+    public void service(Request req, final Response res)
+    throws Exception {
+        try {           
+            String uri=req.requestURI().toString();
+            if( uri.equals("/") ) uri="index.html";
+            String ctx="";
+            String local=uri;
+            if( uri.length() > 1 ) {
+                int idx=uri.indexOf('/', 1);
+                if( idx > 0 ) {
+                    ctx=uri.substring(0, idx);
+                    local=uri.substring( idx );
+                }
+            }
+            Adapter h=(Adapter)prefixMap.get( ctx );
+            if( h != null ) {
+                h.service( req, res );
+            } else {
+                defaultAdapter.service( req, res );
+            }
+        } catch( Throwable t ) {
+            t.printStackTrace();
+        } 
+
+        //out.flushBuffer();
+        //out.getByteChunk().flushBuffer(); - part of res.finish()
+        // final processing
+        MessageWriter.getWriter(req, res, 0).flush();
+        res.finish();
+
+        req.recycle();
+        res.recycle();
+
+    }
+
+    /** Load the handler table. Just a hack, I'll find a better solution
+     */
+    public void initHandlers() {
+
+        prefixMap=new Hashtable();
+        Enumeration keys=System.getProperties().keys();
+
+        Loader loader=null;
+        if( loader == null ) {
+            // Not started from loader, we are embedded - create the loader, 
so we
+            // can do reloading.
+            //LoaderProperties.setPropertiesFile("");
+            try {
+                loader=new Loader();
+                ClassLoader myL=this.getClass().getClassLoader();
+                loader.setParentClassLoader( myL );
+                loader.init();
+            } catch( Throwable t ) {
+                t.printStackTrace();
+            }
+        }
+
+        Repository sR=loader.getRepository("shared");
+        // Construct handlers. Handlers will be created, they can get the 
protocol
+        // if they need additional init
+
+        while( keys.hasMoreElements()) {
+            String n=(String)keys.nextElement();
+            if( n.startsWith("handler.")) {
+                String cls=System.getProperty( n );
+                String map=n.substring(8);
+                Adapter hC=null;
+                try {
+                    // use the loader's server common repository
+                    Class c=sR.getClassLoader().loadClass(cls);
+                    //Class c=Class.forName(cls);
+                    hC=(Adapter)c.newInstance();
+                    prefixMap.put( map, hC );
+                } catch( Throwable t ) {
+                    t.printStackTrace();
+                }
+            }
+        }
+    }
+
+    public void addAdapter( String prefix, Adapter adapter ) {
+        prefixMap.put(prefix, adapter);
+    }
+    
+    public void setDefaultAdapter(Adapter adapter) {
+        defaultAdapter=adapter;
+    }
+
+    public Adapter getDefaultAdapter() {
+        return defaultAdapter;
+    }
+
+}
\ No newline at end of file

Added: 
tomcat/sandbox/java/org/apache/coyote/standalone/ClientAbortException.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/standalone/ClientAbortException.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/standalone/ClientAbortException.java 
(added)
+++ tomcat/sandbox/java/org/apache/coyote/standalone/ClientAbortException.java 
Sat Nov  5 19:13:06 2005
@@ -0,0 +1,144 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.coyote.standalone;
+
+import java.io.IOException;
+
+/**
+ * Wrap an IOException identifying it as being caused by an abort
+ * of a request by a remote client.
+ *
+ * @author Glenn L. Nielsen
+ * @version $Revision: 304063 $ $Date: 2005-08-18 06:25:18 -0700 (Thu, 18 Aug 
2005) $
+ */
+
+public final class ClientAbortException extends IOException {
+
+
+    //------------------------------------------------------------ Constructors
+
+
+    /**
+     * Construct a new ClientAbortException with no other information.
+     */
+    public ClientAbortException() {
+
+        this(null, null);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message.
+     *
+     * @param message Message describing this exception
+     */
+    public ClientAbortException(String message) {
+
+        this(message, null);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified throwable.
+     *
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(Throwable throwable) {
+
+        this(null, throwable);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message
+     * and throwable.
+     *
+     * @param message Message describing this exception
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(String message, Throwable throwable) {
+
+        super();
+        this.message = message;
+        this.throwable = throwable;
+
+    }
+
+
+    //------------------------------------------------------ Instance Variables
+
+
+    /**
+     * The error message passed to our constructor (if any)
+     */
+    protected String message = null;
+
+
+    /**
+     * The underlying exception or error passed to our constructor (if any)
+     */
+    protected Throwable throwable = null;
+
+
+    //---------------------------------------------------------- Public Methods
+
+
+    /**
+     * Returns the message associated with this exception, if any.
+     */
+    public String getMessage() {
+
+        return (message);
+
+    }
+
+
+    /**
+     * Returns the cause that caused this exception, if any.
+     */
+    public Throwable getCause() {
+        
+        return (throwable);
+        
+    }
+
+    
+    /**
+     * Return a formatted string that describes this exception.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ClientAbortException:  ");
+        if (message != null) {
+            sb.append(message);
+            if (throwable != null) {
+                sb.append(":  ");
+            }
+        }
+        if (throwable != null) {
+            sb.append(throwable.toString());
+        }
+        return (sb.toString());
+
+    }
+
+
+}

Added: tomcat/sandbox/java/org/apache/coyote/standalone/Main.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/standalone/Main.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/standalone/Main.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/standalone/Main.java Sat Nov  5 
19:13:06 2005
@@ -0,0 +1,66 @@
+package org.apache.coyote.standalone;
+
+import org.apache.coyote.adapters.Counters;
+import org.apache.coyote.adapters.HelloWorldAdapter;
+import org.apache.coyote.adapters.Mapper;
+import org.apache.coyote.http11.Http11BaseProtocol;
+
+
+/** 
+ * Simple example of embeding coyote.
+ * 
+ */
+public class Main  {
+    
+    protected Http11BaseProtocol proto;
+    protected Mapper mainAdapter;
+    
+    public Main() {        
+    }
+
+    public Http11BaseProtocol getProtocol() {
+        return proto;
+    }
+    
+    public void init() {
+        proto = new Http11BaseProtocol();
+
+        mainAdapter = new Mapper();        
+        mainAdapter.addAdapter("/hello", new HelloWorldAdapter());
+
+        Counters cnt=new Counters();
+        cnt.setNext( mainAdapter );
+
+        //proto.setAdapter(mainAdapter);
+        proto.setAdapter(cnt);
+    }
+    
+    /**
+     */
+    public void run() {
+        init();
+        
+        if( proto.getPort() == 0 )
+            proto.setPort(8800);
+        
+        try {
+            proto.init();
+
+            proto.getThreadPool().setDaemon(false);
+            
+            proto.start();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+    
+    // ------------------- Main ---------------------
+    public static void main( String args[]) {
+        Main sa=new Main();
+        sa.run();
+    }
+
+    
+}
\ No newline at end of file

Added: tomcat/sandbox/java/org/apache/coyote/standalone/MessageReader.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/standalone/MessageReader.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/standalone/MessageReader.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/standalone/MessageReader.java Sat Nov 
 5 19:13:06 2005
@@ -0,0 +1,503 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.coyote.standalone;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+
+/**
+ * Refactored from catalina.connector.InputBuffer. Renamed to avoid conflict
+ * with coyote class.
+ * 
+ * TODO: move to coyote package.
+ */
+
+/**
+ * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, adapted to handle input instead of output. This allows 
+ * complete recycling of the facade objects (the ServletInputStream and the
+ * BufferedReader).
+ *
+ * @author Remy Maucherat
+ */
+public class MessageReader extends Reader
+    implements ByteChunk.ByteInputChannel, CharChunk.CharInputChannel,
+               CharChunk.CharOutputChannel {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_ENCODING = 
+        org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+
+    // The buffer can be used for byte[] and char[] reading
+    // ( this is needed to support ServletInputStream and BufferedReader )
+    public final int INITIAL_STATE = 0;
+    public final int CHAR_STATE = 1;
+    public final int BYTE_STATE = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The byte buffer.
+     */
+    private ByteChunk bb;
+
+
+    /**
+     * The chunk buffer.
+     */
+    private CharChunk cb;
+
+
+    /**
+     * State of the output buffer.
+     */
+    private int state = 0;
+
+
+    /**
+     * Number of bytes read.
+     */
+    private int bytesRead = 0;
+
+
+    /**
+     * Number of chars read.
+     */
+    private int charsRead = 0;
+
+
+    /**
+     * Flag which indicates if the input buffer is closed.
+     */
+    private boolean closed = false;
+
+
+    /**
+     * Byte chunk used to input bytes.
+     */
+    private ByteChunk inputChunk = new ByteChunk();
+
+
+    /**
+     * Encoding to use.
+     */
+    private String enc;
+
+
+    /**
+     * Encoder is set.
+     */
+    private boolean gotEnc = false;
+
+
+    /**
+     * List of encoders.
+     */
+    protected HashMap encoders = new HashMap();
+
+
+    /**
+     * Current byte to char converter.
+     */
+    protected B2CConverter conv;
+
+
+    /**
+     * Associated Coyote request.
+     */
+    private Request coyoteRequest;
+
+
+    /**
+     * Buffer position.
+     */
+    private int markPos = -1;
+
+
+    /**
+     * Buffer size.
+     */
+    private int size = -1;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public MessageReader() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     * 
+     * @param size Buffer size to use
+     */
+    public MessageReader(int size) {
+
+        this.size = size;
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteInputChannel(this);
+        cb = new CharChunk(size);
+        cb.setLimit(size);
+        cb.setOptimizedWrite(false);
+        cb.setCharInputChannel(this);
+        cb.setCharOutputChannel(this);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Coyote request.
+     * 
+     * @param coyoteRequest Associated Coyote request
+     */
+    public void setRequest(Request coyoteRequest) {
+       this.coyoteRequest = coyoteRequest;
+    }
+
+
+    /**
+     * Get associated Coyote request.
+     * 
+     * @return the associated Coyote request
+     */
+    public Request getRequest() {
+        return this.coyoteRequest;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+        
+        state = INITIAL_STATE;
+        bytesRead = 0;
+        charsRead = 0;
+        
+        // If usage of mark made the buffer too big, reallocate it
+        if (cb.getChars().length > size) {
+            cb = new CharChunk(size);
+            cb.setLimit(size);
+            cb.setCharInputChannel(this);
+            cb.setCharOutputChannel(this);
+        } else {
+            cb.recycle();
+        }
+        markPos = -1;
+        bb.recycle(); 
+        closed = false;
+        
+        if (conv != null) {
+            conv.recycle();
+        }
+        
+        gotEnc = false;
+        enc = null;
+        
+    }
+
+
+    /**
+     * Close the input buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+        closed = true;
+    }
+
+
+    public int available()
+        throws IOException {
+        if (state == BYTE_STATE) {
+            return bb.getLength();
+        } else if (state == CHAR_STATE) {
+            return cb.getLength();
+        } else {
+            return 0;
+        }
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Reads new bytes in the byte chunk.
+     * 
+     * @param cbuf Byte buffer to be written to the response
+     * @param off Offset
+     * @param len Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public int realReadBytes(byte cbuf[], int off, int len)
+       throws IOException {
+
+        if (closed)
+            return -1;
+        if (coyoteRequest == null)
+            return -1;
+
+        state = BYTE_STATE;
+
+        int result = coyoteRequest.doRead(bb);
+
+        return result;
+
+    }
+
+
+    public int readByte()
+        throws IOException {
+        return bb.substract();
+    }
+
+
+    public int read(byte[] b, int off, int len)
+        throws IOException {
+        return bb.substract(b, off, len);
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    /**
+     * Since the converter will use append, it is possible to get chars to
+     * be removed from the buffer for "writing". Since the chars have already
+     * been read before, they are ignored. If a mark was set, then the
+     * mark is lost.
+     */
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+        markPos = -1;
+    }
+
+
+    public void setEncoding(String s) {
+        enc = s;
+    }
+
+
+    public int realReadChars(char cbuf[], int off, int len)
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+        if (bb.getLength() <= 0) {
+            int nRead = realReadBytes(bb.getBytes(), 0, bb.getBytes().length);
+            if (nRead < 0) {
+                return -1;
+            }
+        }
+
+        if (markPos == -1) {
+            cb.setOffset(0);
+            cb.setEnd(0);
+        }
+
+        conv.convert(bb, cb);
+        bb.setOffset(bb.getEnd());
+        state = CHAR_STATE;
+
+        return cb.getLength();
+
+    }
+
+
+    public int read()
+        throws IOException {
+        return cb.substract();
+    }
+
+
+    public int read(char[] cbuf)
+        throws IOException {
+        return read(cbuf, 0, cbuf.length);
+    }
+
+
+    public int read(char[] cbuf, int off, int len)
+        throws IOException {
+        return cb.substract(cbuf, off, len);
+    }
+
+
+    public long skip(long n)
+        throws IOException {
+
+        if (n < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        long nRead = 0;
+        while (nRead < n) {
+            if (cb.getLength() >= n) {
+                cb.setOffset(cb.getStart() + (int) n);
+                nRead = n;
+            } else {
+                nRead += cb.getLength();
+                cb.setOffset(cb.getEnd());
+                int toRead = 0;
+                if (cb.getChars().length < (n - nRead)) {
+                    toRead = cb.getChars().length;
+                } else {
+                    toRead = (int) (n - nRead);
+                }
+                int nb = realReadChars(cb.getChars(), 0, toRead);
+                if (nb < 0)
+                    break;
+            }
+        }
+
+        return nRead;
+
+    }
+
+
+    public boolean ready()
+        throws IOException {
+        return (cb.getLength() > 0);
+    }
+
+
+    public boolean markSupported() {
+        return true;
+    }
+
+
+    public void mark(int readAheadLimit)
+        throws IOException {
+        if (cb.getLength() <= 0) {
+            cb.setOffset(0);
+            cb.setEnd(0);
+        } else {
+            if ((cb.getBuffer().length > (2 * size)) 
+                && (cb.getLength()) < (cb.getStart())) {
+                System.arraycopy(cb.getBuffer(), cb.getStart(), 
+                                 cb.getBuffer(), 0, cb.getLength());
+                cb.setEnd(cb.getLength());
+                cb.setOffset(0);
+            }
+        }
+        int offset = readAheadLimit;
+        if (offset < size) {
+            offset = size;
+        }
+        cb.setLimit(cb.getStart() + offset);
+        markPos = cb.getStart();
+    }
+
+
+    public void reset()
+        throws IOException {
+        if (state == CHAR_STATE) {
+            if (markPos < 0) {
+                cb.recycle();
+                markPos = -1;
+                throw new IOException();
+            } else {
+                cb.setOffset(markPos);
+            }
+        } else {
+            bb.recycle();
+        }
+    }
+
+
+    public void checkConverter() 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+    }
+
+
+    protected void setConverter()
+        throws IOException {
+
+        if (coyoteRequest != null)
+            enc = coyoteRequest.getCharacterEncoding();
+
+        gotEnc = true;
+        if (enc == null)
+            enc = DEFAULT_ENCODING;
+        conv = (B2CConverter) encoders.get(enc);
+        if (conv == null) {
+            if (packageDefinitionEnabled && System.getSecurityManager() !=  
null) {
+                //SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    conv = (B2CConverter)AccessController.doPrivileged(
+                            new PrivilegedExceptionAction(){
+
+                                public Object run() throws IOException{
+                                    return new B2CConverter(enc);
+                                }
+
+                            }
+                    );              
+                }catch(PrivilegedActionException ex){
+                    Exception e = ex.getException();
+                    if (e instanceof IOException)
+                        throw (IOException)e; 
+                }
+            } else {
+                conv = new B2CConverter(enc);
+            }
+            encoders.put(enc, conv);
+        }
+
+    }
+
+    private static boolean packageDefinitionEnabled =  
+        (System.getProperty("package.definition") == null && 
+          System.getProperty("package.access")  == null) ? false : true;
+
+}

Added: tomcat/sandbox/java/org/apache/coyote/standalone/MessageWriter.java
URL: 
http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/standalone/MessageWriter.java?rev=331062&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/standalone/MessageWriter.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/standalone/MessageWriter.java Sat Nov 
 5 19:13:06 2005
@@ -0,0 +1,677 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.coyote.standalone;
+
+
+import java.io.IOException;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/*
+ * Refactoring: original code in catalina.connector.
+ * - renamed to OutputWriter to avoid confusion with coyote OutputBuffer
+ * - 
+ * TODO: move it to coyote, add Response.getWriter 
+ * 
+ */
+
+/**
+ * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, with the removal of some of the state handling (which in 
+ * Coyote is mostly the Processor's responsability).
+ *
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class MessageWriter extends Writer
+    implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
+
+    // used in getWriter, until a method is added to res.
+    private static final int WRITER_NOTE = 3;
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_ENCODING = 
+        org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+
+
+    // The buffer can be used for byte[] and char[] writing
+    // ( this is needed to support ServletOutputStream and for
+    // efficient implementations of templating systems )
+    public final int INITIAL_STATE = 0;
+    public final int CHAR_STATE = 1;
+    public final int BYTE_STATE = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The byte buffer.
+     */
+    private ByteChunk bb;
+
+
+    /**
+     * The chunk buffer.
+     */
+    private CharChunk cb;
+
+
+    /**
+     * State of the output buffer.
+     */
+    private int state = 0;
+
+
+    /**
+     * Number of bytes written.
+     */
+    private int bytesWritten = 0;
+
+
+    /**
+     * Number of chars written.
+     */
+    private int charsWritten = 0;
+
+
+    /**
+     * Flag which indicates if the output buffer is closed.
+     */
+    private boolean closed = false;
+
+
+    /**
+     * Do a flush on the next operation.
+     */
+    private boolean doFlush = false;
+
+
+    /**
+     * Byte chunk used to output bytes.
+     */
+    private ByteChunk outputChunk = new ByteChunk();
+
+
+    /**
+     * Encoding to use.
+     */
+    private String enc;
+
+
+    /**
+     * Encoder is set.
+     */
+    private boolean gotEnc = false;
+
+
+    /**
+     * List of encoders.
+     */
+    protected HashMap encoders = new HashMap();
+
+
+    /**
+     * Current char to byte converter.
+     */
+    protected C2BConverter conv;
+
+
+    /**
+     * Associated Coyote response.
+     */
+    private Response coyoteResponse;
+
+
+    /**
+     * Suspended flag. All output bytes will be swallowed if this is true.
+     */
+    private boolean suspended = false;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public MessageWriter() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     * 
+     * @param size Buffer size to use
+     */
+    public MessageWriter(int size) {
+
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteOutputChannel(this);
+        cb = new CharChunk(size);
+        cb.setCharOutputChannel(this);
+        cb.setLimit(size);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Coyote response.
+     * 
+     * @param coyoteResponse Associated Coyote response
+     */
+    public void setResponse(Response coyoteResponse) {
+       this.coyoteResponse = coyoteResponse;
+    }
+
+
+    /**
+     * Get associated Coyote response.
+     * 
+     * @return the associated Coyote response
+     */
+    public Response getResponse() {
+        return this.coyoteResponse;
+    }
+
+
+    /**
+     * Is the response output suspended ?
+     * 
+     * @return suspended flag value
+     */
+    public boolean isSuspended() {
+        return this.suspended;
+    }
+
+
+    /**
+     * Set the suspended flag.
+     * 
+     * @param suspended New suspended flag value
+     */
+    public void setSuspended(boolean suspended) {
+        this.suspended = suspended;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+        
+        state = INITIAL_STATE;
+        bytesWritten = 0;
+        charsWritten = 0;
+        
+        cb.recycle();
+        bb.recycle(); 
+        closed = false;
+        suspended = false;
+        
+        if (conv!= null) {
+            conv.recycle();
+        }
+        
+        gotEnc = false;
+        enc = null;
+        
+    }
+
+
+    /**
+     * Close the output buffer. This tries to calculate the response size if 
+     * the response has not been committed yet.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+
+        if (closed)
+            return;
+        if (suspended)
+            return;
+
+        if ((!coyoteResponse.isCommitted()) 
+            && (coyoteResponse.getContentLengthLong() == -1)) {
+            // Flushing the char buffer
+            if (state == CHAR_STATE) {
+                cb.flushBuffer();
+                state = BYTE_STATE;
+            }
+            // If this didn't cause a commit of the response, the final content
+            // length can be calculated
+            if (!coyoteResponse.isCommitted()) {
+                coyoteResponse.setContentLength(bb.getLength());
+            }
+        }
+
+        doFlush(false);
+        closed = true;
+
+        coyoteResponse.finish();
+
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void flush()
+        throws IOException {
+        doFlush(true);
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    protected void doFlush(boolean realFlush)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        doFlush = true;
+        if (state == CHAR_STATE) {
+            cb.flushBuffer();
+            bb.flushBuffer();
+            state = BYTE_STATE;
+        } else if (state == BYTE_STATE) {
+            bb.flushBuffer();
+        } else if (state == INITIAL_STATE) {
+            // If the buffers are empty, commit the response header
+            coyoteResponse.sendHeaders();
+        }
+        doFlush = false;
+
+        if (realFlush) {
+            coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH, 
+                                  coyoteResponse);
+            // If some exception occurred earlier, or if some IOE occurred
+            // here, notify the servlet with an IOE
+            if (coyoteResponse.isExceptionPresent()) {
+                throw new ClientAbortException
+                    (coyoteResponse.getErrorException());
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Sends the buffer data to the client output, checking the
+     * state of Response and calling the right interceptors.
+     * 
+     * @param buf Byte buffer to be written to the response
+     * @param off Offset
+     * @param cnt Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void realWriteBytes(byte buf[], int off, int cnt)
+       throws IOException {
+
+        if (closed)
+            return;
+        if (coyoteResponse == null)
+            return;
+
+        // If we really have something to write
+        if (cnt > 0) {
+            // real write to the adapter
+            outputChunk.setBytes(buf, off, cnt);
+            try {
+                coyoteResponse.doWrite(outputChunk);
+            } catch (IOException e) {
+                // An IOException on a write is almost always due to
+                // the remote client aborting the request.  Wrap this
+                // so that it can be handled better by the error dispatcher.
+                throw new ClientAbortException(e);
+            }
+        }
+
+    }
+
+
+    public void write(byte b[], int off, int len) throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+        writeBytes(b, off, len);
+
+    }
+
+
+    private void writeBytes(byte b[], int off, int len) 
+        throws IOException {
+
+        if (closed)
+            return;
+
+        bb.append(b, off, len);
+        bytesWritten += len;
+
+        // if called from within flush(), then immediately flush
+        // remaining bytes
+        if (doFlush) {
+            bb.flushBuffer();
+        }
+
+    }
+
+
+    public void writeByte(int b)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+
+        bb.append( (byte)b );
+        bytesWritten++;
+
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    public void write(int c)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        cb.append((char) c);
+        charsWritten++;
+
+    }
+
+
+    public void write(char c[])
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        write(c, 0, c.length);
+
+    }
+
+
+    public void write(char c[], int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        cb.append(c, off, len);
+        charsWritten += len;
+
+    }
+
+
+    public void write(StringBuffer sb)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        int len = sb.length();
+        charsWritten += len;
+        cb.append(sb);
+
+    }
+
+
+    /**
+     * Append a string to the buffer
+     */
+    public void write(String s, int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state=CHAR_STATE;
+
+        charsWritten += len;
+        if (s==null)
+            s="null";
+        cb.append( s, off, len );
+
+    }
+
+
+    public void write(String s)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+        if (s==null)
+            s="null";
+        write(s, 0, s.length());
+
+    } 
+
+
+    public void flushChars()
+        throws IOException {
+
+        cb.flushBuffer();
+        state = BYTE_STATE;
+
+    }
+
+
+    public boolean flushCharsNeeded() {
+        return state == CHAR_STATE;
+    }
+
+
+    public void setEncoding(String s) {
+        enc = s;
+    }
+
+
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+        conv.convert(c, off, len);
+        conv.flushBuffer();    // ???
+
+    }
+
+
+    public void checkConverter() 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+    }
+
+
+    protected void setConverter() 
+        throws IOException {
+
+        if (coyoteResponse != null)
+            enc = coyoteResponse.getCharacterEncoding();
+
+        gotEnc = true;
+        if (enc == null)
+            enc = DEFAULT_ENCODING;
+        conv = (C2BConverter) encoders.get(enc);
+        if (conv == null) {
+            
+            if (System.getSecurityManager() != null){
+                try{
+                    conv = (C2BConverter)AccessController.doPrivileged(
+                            new PrivilegedExceptionAction(){
+
+                                public Object run() throws IOException{
+                                    return new C2BConverter(bb, enc);
+                                }
+
+                            }
+                    );              
+                }catch(PrivilegedActionException ex){
+                    Exception e = ex.getException();
+                    if (e instanceof IOException)
+                        throw (IOException)e; 
+                }
+            } else {
+                conv = new C2BConverter(bb, enc);
+            }
+            
+            encoders.put(enc, conv);
+
+        }
+    }
+
+    
+    // --------------------  BufferedOutputStream compatibility
+
+
+    /**
+     * Real write - this buffer will be sent to the client
+     */
+    public void flushBytes()
+        throws IOException {
+
+        bb.flushBuffer();
+
+    }
+
+
+    public int getBytesWritten() {
+        return bytesWritten;
+    }
+
+
+    public int getCharsWritten() {
+        return charsWritten;
+    }
+
+
+    public int getContentWritten() {
+        return bytesWritten + charsWritten;
+    }
+
+
+    /** 
+     * True if this buffer hasn't been used ( since recycle() ) -
+     * i.e. no chars or bytes have been added to the buffer.  
+     */
+    public boolean isNew() {
+        return (bytesWritten == 0) && (charsWritten == 0);
+    }
+
+
+    public void setBufferSize(int size) {
+        if (size > bb.getLimit()) {// ??????
+           bb.setLimit(size);
+       }
+    }
+
+
+    public void reset() {
+
+        //count=0;
+        bb.recycle();
+        bytesWritten = 0;
+        cb.recycle();
+        charsWritten = 0;
+        gotEnc = false;
+        enc = null;
+        state = INITIAL_STATE;
+    }
+
+
+    public int getBufferSize() {
+       return bb.getLimit();
+    }
+
+
+    public static MessageWriter getWriter(Request req, Response res, int size) 
+    {        
+        MessageWriter 
out=(MessageWriter)req.getNote(MessageWriter.WRITER_NOTE);
+        if( out == null ) {
+            if( size<=0 ) {
+                out=new MessageWriter();
+            } else {
+                out=new MessageWriter(size);
+            }
+            out.setResponse(res);
+            req.setNote(MessageWriter.WRITER_NOTE, out );
+        }
+        return out;
+    }
+
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to