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]