From: costin Date: Thu, 26 Nov 2009 06:48:42 +0000 (+0000) Subject: Moved the old coyote-specific servlet api impl to coyote/servlet X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=5d977af9576fb86f82cb08840356be670c7f1d10;p=tomcat7.0 Moved the old coyote-specific servlet api impl to coyote/servlet Added (10% working) coyote connector that uses the new http impl git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@884418 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/tomcat-lite/java/org/apache/coyote/lite/LiteProtocolHandler.java b/modules/tomcat-lite/java/org/apache/coyote/lite/LiteProtocolHandler.java new file mode 100644 index 000000000..1c6da20ef --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/lite/LiteProtocolHandler.java @@ -0,0 +1,151 @@ +package org.apache.coyote.lite; + + + +import java.io.IOException; +import java.util.Iterator; + +import org.apache.coyote.Adapter; +import org.apache.coyote.InputBuffer; +import org.apache.coyote.OutputBuffer; +import org.apache.coyote.ProtocolHandler; +import org.apache.coyote.Request; +import org.apache.coyote.Response; +import org.apache.tomcat.lite.http.HttpConnector; +import org.apache.tomcat.lite.http.HttpRequest; +import org.apache.tomcat.lite.http.HttpResponse; +import org.apache.tomcat.lite.http.HttpChannel.HttpService; +import org.apache.tomcat.lite.io.CBuffer; +import org.apache.tomcat.lite.io.SocketConnector; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.MessageBytes; + +/** + * Work in progress - use the refactored http as a coyote connector. + * Just basic requests work right now - need to implement all the + * methods of coyote. + * + * + * @author Costin Manolache + */ +public class LiteProtocolHandler implements ProtocolHandler { + + Adapter adapter; + + @Override + public void destroy() throws Exception { + } + + @Override + public Adapter getAdapter() { + return adapter; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Iterator getAttributeNames() { + return null; + } + + @Override + public void init() throws Exception { + } + + @Override + public void pause() throws Exception { + } + + @Override + public void resume() throws Exception { + } + + @Override + public void setAdapter(Adapter adapter) { + this.adapter = adapter; + + } + + int port = 8999; + + public void setPort(int port) { + this.port = port; + } + + @Override + public void setAttribute(String name, Object value) { + System.err.println("setAttribute " + name + " " + value); + } + + @Override + public void start() throws Exception { + HttpConnector c = new HttpConnector(new SocketConnector()); + c.setPort(port); + +// c.setDebug(true); +// c.setDebugHttp(true); + + c.getDispatcher().setDefaultService(new HttpService() { + @Override + public void service(HttpRequest httpReq, HttpResponse httpRes) + throws IOException { + coyoteService(httpReq, httpRes); + } + + }); + c.start(); + } + + private void wrap(MessageBytes dest, CBuffer buffer) { + dest.setChars(buffer.array(), buffer.position(), + buffer.length()); + } + + private void coyoteService(HttpRequest httpReq, final HttpResponse httpRes) { + Request req = new Request(); + req.setInputBuffer(new InputBuffer() { + @Override + public int doRead(ByteChunk chunk, Request request) + throws IOException { + // TODO + return 0; + } + }); + Response res = new Response(); + res.setOutputBuffer(new OutputBuffer() { + + @Override + public int doWrite(org.apache.tomcat.util.buf.ByteChunk chunk, + Response response) throws IOException { + httpRes.getBody().append(chunk.getBuffer(), chunk.getStart(), + chunk.getLength()); + return chunk.getLength(); + } + + }); + + // TODO: turn http request into a coyote request - copy all fields, + // add hooks where needed. + + wrap(req.decodedURI(), httpReq.decodedURI()); + wrap(req.method(), httpReq.method()); + wrap(req.protocol(), httpReq.protocol()); + wrap(req.requestURI(), httpReq.requestURI()); + // Same for response. + + try { + + adapter.service(req, res); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/BodyReader.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/BodyReader.java new file mode 100644 index 000000000..228edeade --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/BodyReader.java @@ -0,0 +1,509 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.HashMap; + +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. + * + */ + +/** + * 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 BodyReader 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. More data may be added to it while reading. + */ + private ByteChunk bb; + + + /** + * The chunk buffer, will be filled in from the bb. + */ + 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; + + /** + * Encoding to use. + */ + private String enc; + + + /** + * Encoder is set. + */ + private boolean gotEnc = false; + + + /** + * Cached encoders. + */ + protected HashMap encoders = + new HashMap(); + + + /** + * Current byte to char converter. + */ + protected B2CConverter conv; + + + /** + * Associated Coyote request. + */ + private ServletRequestImpl coyoteRequest; + Connector connector; + + /** + * Buffer position. + */ + private int markPos = -1; + + + /** + * Buffer size. + */ + private int size = -1; + + + // ----------------------------------------------------------- Constructors + + + /** + * Default constructor. Allocate the buffer with the default buffer size. + */ + public BodyReader() { + this(DEFAULT_BUFFER_SIZE); + } + + + /** + * Alternate constructor which allows specifying the initial buffer size. + * + * @param size Buffer size to use + */ + public BodyReader(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 setConnector(Connector c, ServletRequestImpl coyoteRequest) { + this.connector = c; + this.coyoteRequest = 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 = connector.doRead(coyoteRequest, 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; + } + + /** + * Called when a read(char[]) operation is lacking data. It will read + * bytes. + */ + 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, -1); + 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(); + } + } + + + 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) { + conv = new B2CConverter(enc); + encoders.put(enc, conv); + } + } + + public class MRInputStream extends InputStream { + public long skip(long n) + throws IOException { + return BodyReader.this.skip(n); + } + + public void mark(int readAheadLimit) + { + try { + BodyReader.this.mark(readAheadLimit); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public void reset() + throws IOException { + BodyReader.this.reset(); + } + + + + public int read() + throws IOException { + return BodyReader.this.readByte(); + } + + public int available() throws IOException { + return BodyReader.this.available(); + } + + public int read(final byte[] b) throws IOException { + return BodyReader.this.read(b, 0, b.length); + } + + + public int read(final byte[] b, final int off, final int len) + throws IOException { + + return BodyReader.this.read(b, off, len); + } + + + /** + * Close the stream + * Since we re-cycle, we can't allow the call to super.close() + * which would permantely disable us. + */ + public void close() throws IOException { + BodyReader.this.close(); + } + } + + MRInputStream is = new MRInputStream(); + + public InputStream asInputStream() { + return is; + } +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/BodyWriter.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/BodyWriter.java new file mode 100644 index 000000000..e88097f1b --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/BodyWriter.java @@ -0,0 +1,671 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +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.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.C2BConverter; +import org.apache.tomcat.util.buf.CharChunk; + +/** + * Implement buffering and character translation acording to the + * servlet spec. + * + * This class handles both chars and bytes. + * + * It is tightly integrated with servlet response, sending headers + * and updating the commit state. + * + * TODO: add 'extension' interface that allows direct access to + * the async connector non-copy non-blocking queue. Same for the + * OutputStream. Maybe switch the buffer to the brigade. + * + * @author Costin Manolache + */ +public class BodyWriter extends Writer { + + // used in getWriter, until a method is added to res. + protected static final int WRITER_NOTE = 3; + + + private ByteChunk.ByteOutputChannel byteFlusher = + new ByteChunk.ByteOutputChannel() { + + @Override + public void realWriteBytes(byte[] cbuf, int off, int len) + throws IOException { + BodyWriter.this.realWriteBytes(cbuf, off, len); + } + }; + + private CharChunk.CharOutputChannel charFlusher = + new CharChunk.CharOutputChannel() { + @Override + public void realWriteChars(char[] cbuf, int off, int len) + throws IOException { + BodyWriter.this.realWriteChars(cbuf, off, len); + } + }; + + + 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 CHAR_STATE = 1; + public final int BYTE_STATE = 2; + + boolean headersSent = false; + // ----------------------------------------------------- Instance Variables + ServletResponseImpl res; + + /** + * The byte buffer. + */ + protected ByteChunk bb; + + + /** + * The chunk buffer. + */ + protected CharChunk cb; + + + /** + * State of the output buffer. + */ + protected int state = 0; + + + /** + * Number of bytes written. + */ + protected int bytesWritten = 0; + + + /** + * Number of chars written. + */ + protected int charsWritten = 0; + + + /** + * Flag which indicates if the output buffer is closed. + */ + protected boolean closed = false; + + + /** + * Do a flush on the next operation. + */ + protected boolean doFlush = false; + + + /** + * Byte chunk used to output bytes. This is just used to wrap the byte[] + * to match the coyote OutputBuffer interface + */ + protected ByteChunk outputChunk = new ByteChunk(); + + + /** + * Encoding to use. + * TODO: isn't it redundant ? enc, gotEnc, conv plus the enc in the bb + */ + protected String enc; + + + /** + * Encoder is set. + */ + protected boolean gotEnc = false; + + + /** + * List of encoders. The writer is reused - the encoder mapping + * avoids creating expensive objects. In future it'll contain nio.Charsets + */ + protected HashMap encoders = new HashMap(); + + + /** + * Current char to byte converter. TODO: replace with Charset + */ + protected C2BConverter conv; + + /** + * Suspended flag. All output bytes will be swallowed if this is true. + */ + protected boolean suspended = false; + + private Connector connector; + + + // ----------------------------------------------------------- Constructors + + + /** + * Default constructor. Allocate the buffer with the default buffer size. + */ + public BodyWriter() { + + this(DEFAULT_BUFFER_SIZE); + + } + + + /** + * Alternate constructor which allows specifying the initial buffer size. + * + * @param size Buffer size to use + */ + public BodyWriter(int size) { + + bb = new ByteChunk(size); + bb.setLimit(size); + bb.setByteOutputChannel(byteFlusher); + cb = new CharChunk(size); + cb.setCharOutputChannel(charFlusher); + cb.setLimit(size); + + } + + public void setConnector(Connector c, ServletResponseImpl res) { + this.res = res; + this.connector = c; + } + + + // ------------------------------------------------------------- Properties + + + /** + * 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 = BYTE_STATE; + headersSent = false; + 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 (state == CHAR_STATE) { + cb.flushBuffer(); + state = BYTE_STATE; + } + connector.beforeClose(res, bb.getLength()); + + doFlush(false); + closed = true; + + connector.finishResponse(res); + } + + + /** + * 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 (!headersSent) { + // If the buffers are empty, commit the response header + connector.sendHeaders(res); + headersSent = true; + } + if (state == CHAR_STATE) { + cb.flushBuffer(); + state = BYTE_STATE; + } + if (state == BYTE_STATE) { + bb.flushBuffer(); + } + doFlush = false; + + if (realFlush) { + connector.realFlush(res); + } + + } + + + // ------------------------------------------------- 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 + */ + private void realWriteBytes(byte buf[], int off, int cnt) + throws IOException { + + if (closed) + return; + + // If we really have something to write + if (cnt > 0) { + // real write to the adapter + outputChunk.setBytes(buf, off, cnt); + try { + connector.doWrite(res, 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(StringBuilder sb) + throws IOException { + + if (suspended) + return; + + state = CHAR_STATE; + + int len = sb.length(); + charsWritten += len; + cb.append(sb.toString()); + + } + + + /** + * 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 println() throws IOException { + write("\n"); + } + + public void println(String s) throws IOException { + write(s); + write("\n"); + } + + public void print(String s) throws IOException { + write(s); + } + + public void flushChars() + throws IOException { + + cb.flushBuffer(); + state = BYTE_STATE; + + } + + + public boolean flushCharsNeeded() { + return state == CHAR_STATE; + } + + + public void setEncoding(String s) { + enc = s; + } + + + private 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 { + + enc = res.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 = BYTE_STATE; + } + + + public int getBufferSize() { + return bb.getLimit(); + } + + public ByteChunk getByteBuffer() { + return outputChunk; + } + +} +//{ +// public abstract int getBytesWritten(); +// public abstract int getCharsWritten(); +// public abstract void recycle(); +// public abstract void setSuspended(boolean suspended); +// public abstract boolean isSuspended(); +// +// public abstract void reset(); +// public abstract int getBufferSize(); +// public abstract void setBufferSize(int n); +// public abstract void checkConverter() throws IOException; +// public boolean isNew() { +// return getBytesWritten() == 0 && getCharsWritten() == 0; +// } +// public abstract void write(byte[] b, int off, int len) throws IOException; +// public abstract void writeByte(int b) throws IOException; +// +//} \ No newline at end of file diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/CharsetMapperDefault.properties b/modules/tomcat-lite/java/org/apache/coyote/servlet/CharsetMapperDefault.properties new file mode 100644 index 000000000..c73a8fc4c --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/CharsetMapperDefault.properties @@ -0,0 +1,2 @@ +en=ISO-8859-1 +fr=ISO-8859-1 diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ClientAbortException.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ClientAbortException.java new file mode 100644 index 000000000..b4c6909bc --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ClientAbortException.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +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() { + + StringBuilder sb = new StringBuilder("ClientAbortException: "); + if (message != null) { + sb.append(message); + if (throwable != null) { + sb.append(": "); + } + } + if (throwable != null) { + sb.append(throwable.toString()); + } + return (sb.toString()); + + } + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/Connector.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/Connector.java new file mode 100644 index 000000000..3a997a907 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/Connector.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tomcat.integration.ObjectManager; +import org.apache.tomcat.util.buf.ByteChunk; + +/** + * What we need to plugin a connector. + * + * Currently we have lots of deps on coyote Request, but I plan to + * change this and allow other HTTP implementations - like MINA, the + * experimental async connector, etc. Most important will be + * different support for IO - i.e. a more non-blocking mode. + * We'll probably keep MessageBytes as wrappers for request/res + * properties. + * + * This interface has no dep on coyote. + * + */ +public interface Connector { + + public void setDaemon(boolean b); + + public void start() throws IOException; + + public void stop() throws Exception; + + /** + * Called during close() - either on explicit output close, or + * after the request is completed. + * + * @throws IOException + */ + public abstract void finishResponse(HttpServletResponse res) throws IOException; + + /** + * Called before flushing the output during close. + * Content-Length may be updated. + * @param len + * + * @throws IOException + */ + public abstract void beforeClose(HttpServletResponse res, int len) throws IOException; + + /** + * Called when the first flush() is called. + * @throws IOException + */ + public abstract void sendHeaders(HttpServletResponse res) throws IOException; + + /** + * Send data to the client. + * @throws IOException + */ + public abstract void realFlush(HttpServletResponse res) throws IOException; + + /** + * Write to the connector underlying buffer. + * The chunk will be reused (currently). + */ + public abstract void doWrite(HttpServletResponse res, ByteChunk outputChunk2) throws IOException; + + + //public void finishResponse(HttpServletResponse res) throws IOException; + + public void acknowledge(HttpServletResponse res) throws IOException; + + public void reset(HttpServletResponse res); + + public void recycle(HttpServletRequest req, HttpServletResponse res); + + void initRequest(HttpServletRequest req, HttpServletResponse res); + + public void setTomcatLite(TomcatLite tomcatLite); + + public void setObjectManager(ObjectManager objectManager); + + public String getRemoteHost(HttpServletRequest req); + + public String getRemoteAddr(HttpServletRequest req); + + public int doRead(ServletRequestImpl coyoteRequest, ByteChunk bb) throws IOException; +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/CoyoteConnector.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/CoyoteConnector.java new file mode 100644 index 000000000..4e8cd528d --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/CoyoteConnector.java @@ -0,0 +1,478 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.coyote.ActionCode; +import org.apache.coyote.ActionHook; +import org.apache.coyote.Adapter; +import org.apache.coyote.ProtocolHandler; +import org.apache.coyote.Request; +import org.apache.coyote.Response; +import org.apache.coyote.http11.Http11NioProtocol; +import org.apache.tomcat.integration.ObjectManager; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.UriNormalizer; +import org.apache.tomcat.util.http.HttpRequest; +import org.apache.tomcat.util.http.HttpResponse; +import org.apache.tomcat.util.net.SocketStatus; + +public class CoyoteConnector implements Adapter, Connector { + + private TomcatLite lite; + + public CoyoteConnector() { + } + + + + public void acknowledge(HttpServletResponse res) throws IOException { + Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; + cres.acknowledge(); + } + + public void reset(HttpServletResponse res) { + Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; + cres.reset(); + } + + public void recycle(HttpServletRequest req, HttpServletResponse res) { + + } + + public static HttpResponse getResponse(final Response cres) { + HttpResponse hres = new HttpResponse() { + public int getStatus() { + return cres.getStatus(); + } + public void setStatus(int i) { + super.setStatus(i); + cres.setStatus(i); + } + public void setMessage(String s) { + super.setMessage(s); + cres.setMessage(s); + } + public String getMessage() { + return cres.getMessage(); + } + public boolean isCommitted() { + return cres.isCommitted(); + } + + public void setCommitted(boolean b) { + cres.setCommitted(b); + } + }; + + hres.setMimeHeaders(cres.getMimeHeaders()); + hres.nativeResponse = cres; + + return hres; + } + + public static HttpRequest getRequest(Request req) { + + HttpRequest httpReq = new HttpRequest(req.scheme(), + req.method(), + req.unparsedURI(), + req.protocol(), + req.getMimeHeaders(), + req.requestURI(), + req.decodedURI(), + req.query(), req.getParameters(), + req.serverName(), + req.getCookies()) { + + }; + httpReq.nativeRequest = req; + + // TODO: anything else computed in coyote ? + + return httpReq; + } + + @Override + public void initRequest(HttpServletRequest hreq, HttpServletResponse hres) { + ServletRequestImpl req = (ServletRequestImpl) hreq; + ServletResponseImpl res = (ServletResponseImpl) hres; + req.setConnector(this); + + Request creq = new Request(); + Response cres = new Response(); + HttpResponse nRes = getResponse(cres); + + BodyWriter out = new BodyWriter(4096); + out.setConnector(this, res); + + res.setHttpResponse(nRes, out); + + cres.setRequest(creq); + cres.setHook(new ActionHook() { + public void action(ActionCode actionCode, + Object param) { + } + }); + + BodyReader in = new BodyReader(); + in.setConnector(this, req); + HttpRequest nReq = getRequest(creq); + req.setHttpRequest(nReq, in); + + } + + + // ---- Coyote Adapter interface --- + + @Override + public void service(Request creq, Response cres) throws Exception { + long t0 = System.currentTimeMillis(); + + // compute decodedURI - not done by connector + UriNormalizer.decodeRequest(creq.decodedURI(), creq.requestURI(), creq.getURLDecoder()); + + // find the facades + ServletRequestImpl req = (ServletRequestImpl) creq.getNote(ADAPTER_REQ_NOTE); + ServletResponseImpl res = (ServletResponseImpl) cres.getNote(ADAPTER_RES_NOTE); + + + if (req == null) { + req = new ServletRequestImpl(); + res = req.getResponse(); + + BodyReader in = new BodyReader(); + in.setConnector(this, req); + + HttpRequest nReq = getRequest(creq); + nReq.setServerPort(creq.getServerPort()); + HttpResponse nRes = getResponse(cres); + + req.setHttpRequest(nReq, in); + BodyWriter out = new BodyWriter(4096); + out.setConnector(this, res); + + res.setHttpResponse(nRes, out); + + creq.setNote(ADAPTER_REQ_NOTE, req); + cres.setNote(ADAPTER_RES_NOTE, res); + + } + req.setConnector(this); + + try { + lite.service(req, res); + } catch(IOException ex) { + throw ex; + } catch( Throwable t ) { + t.printStackTrace(); + } finally { + long t1 = System.currentTimeMillis(); + +// log.info("<<<<<<<< DONE: " + creq.method() + " " + +// creq.decodedURI() + " " + +// res.getStatus() + " " + +// (t1 - t0)); + + // Final processing + // TODO: only if not commet, this doesn't work with the + // other connectors since we don't have the info + // TODO: add this note in the nio/apr connectors + // TODO: play nice with TomcatLite, other adapters that flush/close + if (cres.getNote(COMET_RES_NOTE) == null) { + + if (!res.isCommitted()) { + cres.sendHeaders(); + } + res.getOutputBuffer().flush(); + + BodyWriter mw = res.getBodyWriter(); + //MessageWriter.getWriter(creq, cres, 0); + mw.flush(); + mw.recycle(); + + BodyReader reader = req.getBodyReader(); + //getReader(creq); + reader.recycle(); + + cres.finish(); + + creq.recycle(); + cres.recycle(); + + req.recycle(); + res.recycle(); + } + } + } + + @Override + public boolean event(Request req, Response res, SocketStatus status) + throws Exception { + return false; + } + + + public void setTomcatLite(TomcatLite lite) { + this.lite = lite; + } + + + public String getRemoteHost(HttpServletRequest hreq) { + ServletRequestImpl req = (ServletRequestImpl) hreq; + + Request creq = (Request) req.getHttpRequest().nativeRequest; + creq.action(ActionCode.ACTION_REQ_HOST_ATTRIBUTE, creq); + return creq.remoteHost().toString(); + } + + public String getRemoteAddr(HttpServletRequest hreq) { + ServletRequestImpl req = (ServletRequestImpl) hreq; + + Request creq = (Request) req.getHttpRequest().nativeRequest; + creq.action(ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, creq); + return creq.remoteAddr().toString(); + } + + + @Override + public void beforeClose(HttpServletResponse res, int len) throws IOException { + Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; + + if ((!cres.isCommitted()) + && (cres.getContentLengthLong() == -1)) { + // Flushing the char buffer + // If this didn't cause a commit of the response, the final content + // length can be calculated + if (!cres.isCommitted()) { + cres.setContentLength(len); + } + } + } + + public int doRead(ServletRequestImpl hreq, ByteChunk bb) throws IOException { + ServletRequestImpl req = (ServletRequestImpl) hreq; + + Request creq = (Request) req.getHttpRequest().nativeRequest; + return creq.doRead(bb); + } + + @Override + public void doWrite(HttpServletResponse res, ByteChunk chunk) + throws IOException { + Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; + cres.doWrite(chunk); + + } + + + @Override + public void realFlush(HttpServletResponse res) throws IOException { + Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; + cres.action(ActionCode.ACTION_CLIENT_FLUSH, + cres); + // If some exception occurred earlier, or if some IOE occurred + // here, notify the servlet with an IOE + if (cres.isExceptionPresent()) { + throw new ClientAbortException + (cres.getErrorException()); + } + + } + + + @Override + public void sendHeaders(HttpServletResponse res) throws IOException { + Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; + + // This should happen before 'prepareResponse' is called !! + // Now update coyote response based on response + // don't set charset/locale - they're computed in lite + cres.setContentType(res.getContentType()); + cres.sendHeaders(); + } + + @Override + public void finishResponse(HttpServletResponse res) throws IOException { + Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; + cres.finish(); + } + + protected int port = 8800; + protected boolean daemon = false; + + /** + * Note indicating the response is COMET. + */ + public static final int COMET_RES_NOTE = 2; + public static final int COMET_REQ_NOTE = 2; + + public static final int ADAPTER_RES_NOTE = 1; + public static final int ADAPTER_REQ_NOTE = 1; + + protected ProtocolHandler proto; + + //protected Adapter adapter = new MapperAdapter(); + protected int maxThreads = 20; + boolean started = false; + boolean async = false; // use old nio connector + + protected ObjectManager om; + + + public void setObjectManager(ObjectManager om) { + this.om = om; + } + + /** + * Add an adapter. If more than the 'default' adapter is + * added, a MapperAdapter will be inserted. + * + * @param path Use "/" for the default. + * @param adapter + */ +// public void addAdapter(String path, Adapter added) { +// if ("/".equals(path)) { +// ((MapperAdapter) adapter).setDefaultAdapter(added); +// } else { +// ((MapperAdapter) adapter).getMapper().addWrapper(path, added); +// } +// } + + /** + */ + public void run() { + try { + init(); + start(); + } catch(IOException ex) { + ex.printStackTrace(); + } + } + + public void setDaemon(boolean b) { + daemon = b; + } + + protected void initAdapters() { + if (proto == null) { + addProtocolHandler(port, daemon); + } + // adapter = ... + // Adapter secondaryadapter = ... + //registry.registerComponent(secondaryadapter, ":name=adapter", null); + } + + public void stop() throws Exception { + if (!started) { + return; + } + proto.destroy(); + started = false; + } + +// /** +// * Simple CLI support - arg is a path:className pair. +// */ +// public void setAdapter(String arg) { +// String[] pathClass = arg.split(":", 2); +// try { +// Class c = Class.forName(pathClass[1]); +// Adapter a = (Adapter) c.newInstance(); +// addAdapter(pathClass[0],a); +// } catch (Throwable e) { +// e.printStackTrace(); +// } +// } + + public void setConnector(ProtocolHandler h) { + this.proto = h; + h.setAttribute("port", Integer.toString(port)); + + om.bind("ProtocolHandler:" + "ep-" + port, proto); + } + + public void addProtocolHandler(int port, boolean daemon) { + Http11NioProtocol proto = new Http11NioProtocol(); + proto.setCompression("on"); + proto.setCompressionMinSize(32); + proto.setPort(port); + //proto.getEndpoint().setDaemon(daemon); + setConnector(proto); + setPort(port); + setDaemon(daemon); + } + + public void addProtocolHandler(ProtocolHandler proto, + int port, boolean daemon) { + setConnector(proto); + setPort(port); + setDaemon(daemon); + } + + public void setPort(int port) { + if (proto != null) { + proto.setAttribute("port", Integer.toString(port)); + } + this.port = port; + } + + + public void init() { + //JdkLoggerConfig.loadCustom(); + om.bind("CoyoteConnector:" + "CoyoteConnector-" + port, + this); + } + + + public void start() throws IOException { + try { + if (started) { + return; + } + init(); + initAdapters(); + + // not required - should run fine without a connector. + if (proto != null) { + proto.setAdapter(this); + + proto.init(); + proto.start(); + } + + started = true; + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public boolean getStarted() { + return started; + } + + public boolean asyncDispatch(Request req,Response res, SocketStatus status) throws Exception { + // implement me + return false; + } + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/FilterChainImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/FilterChainImpl.java new file mode 100644 index 000000000..7cbb54af8 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/FilterChainImpl.java @@ -0,0 +1,168 @@ +/* + * Copyright 2009 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.servlet; + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +/** + * Wraps the list of filters for the current request. One instance + * associated with each RequestImpl, reused. + * + * Populated by the mapper ( WebappFilterMapper for example ), which + * determines the filters for the current request. + * + * Not thread safe. + */ +public final class FilterChainImpl implements FilterChain { + private List filters = new ArrayList(); + + + /** + * The int which is used to maintain the current position + * in the filter chain. + */ + private int pos = 0; + + /** + * The servlet instance to be executed by this chain. + */ + private Servlet servlet = null; + + + private ServletConfigImpl wrapper; + + + public FilterChainImpl() { + super(); + } + + + /** + * Invoke the next filter in this chain, passing the specified request + * and response. If there are no more filters in this chain, invoke + * the service() method of the servlet itself. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet exception occurs + */ + public void doFilter(ServletRequest request, ServletResponse response) + throws IOException, ServletException { + + + // Call the next filter if there is one + if (pos < filters.size()) { + FilterConfigImpl filterConfig = filters.get(pos++); + Filter filter = null; + try { + filter = filterConfig.getFilter(); + filter.doFilter(request, response, this); + } catch (IOException e) { + throw e; + } catch (ServletException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + e.printStackTrace(); + throw new ServletException("Throwable", e); + } + return; + } + + // We fell off the end of the chain -- call the servlet instance + try { + if (servlet != null) + servlet.service(request, response); + } catch (IOException e) { + throw e; + } catch (ServletException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new ServletException("Throwable", e); + } + } + + + // -------------------------------------------------------- Package Methods + + + + /** + * Add a filter to the set of filters that will be executed in this chain. + * + * @param filterConfig The FilterConfig for the servlet to be executed + */ + public void addFilter(FilterConfigImpl filterConfig) { + filters.add(filterConfig); + } + + + /** + * Release references to the filters and wrapper executed by this chain. + */ + public void release() { + filters.clear(); + pos = 0; + servlet = null; + } + + + /** + * Set the servlet that will be executed at the end of this chain. + * Set by the mapper filter + */ + public void setServlet(ServletConfigImpl wrapper, Servlet servlet) { + this.wrapper = wrapper; + this.servlet = servlet; + } + + // ------ Getters for information ------------ + + public int getSize() { + return filters.size(); + } + + public FilterConfigImpl getFilter(int i) { + return filters.get(i); + } + + public Servlet getServlet() { + return servlet; + } + + public ServletConfigImpl getServletConfig() { + return wrapper; + } + + public int getPos() { + return pos; + } +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/FilterConfigImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/FilterConfigImpl.java new file mode 100644 index 000000000..2baf77af9 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/FilterConfigImpl.java @@ -0,0 +1,347 @@ +/* + * 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.servlet; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterConfig; +import javax.servlet.FilterRegistration; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.FilterRegistration.Dynamic; + +import org.apache.tomcat.servlets.util.Enumerator; + + +/** + * A Filter is configured in web.xml by: + * - name - used in mappings + * - className - used to instantiate the filter + * - init params + * - other things not used in the servlet container ( icon, descr, etc ) + * + * Alternatively, in API mode you can pass the actual filter. + * + * @see ServletConfigImpl + */ +public final class FilterConfigImpl implements FilterConfig, FilterRegistration { + + DynamicFilterRegistration dynamic = new DynamicFilterRegistration(); + + public FilterConfigImpl(ServletContextImpl context) { + this.ctx = context; + } + + boolean asyncSupported; + + private ServletContextImpl ctx = null; + + /** + * The application Filter we are configured for. + */ + private transient Filter filter = null; + + String descryption; + + private String filterName; + + private String filterClassName; + + Map initParams; + + private Class filterClass; + + public void setData(String filterName, String filterClass, + Map params) { + this.filterName = filterName; + this.filterClassName = filterClass; + this.initParams = params; + } + + public void setFilter(Filter f) { + filter = f; + } + + public String getFilterName() { + return filterName; + } + + public void setFilterClass(Class filterClass2) { + this.filterClass = filterClass2; + } + + + public String getInitParameter(String name) { + if (initParams == null) return null; + return initParams.get(name); + } + + /** + * Return an Enumeration of the names of the initialization + * parameters for this Filter. + */ + public Enumeration getInitParameterNames() { + if (initParams == null) + return (new Enumerator(new ArrayList())); + else + return (new Enumerator(initParams.keySet())); + } + + + /** + * Return the ServletContext of our associated web application. + */ + public ServletContext getServletContext() { + return ctx; + } + + /** + * Return the application Filter we are configured for. + */ + public Filter createFilter() throws ClassCastException, ClassNotFoundException, + IllegalAccessException, InstantiationException, ServletException { + + // Return the existing filter instance, if any + if (filter != null) + return filter; + + ClassLoader classLoader = ctx.getClassLoader(); + + ClassLoader oldCtxClassLoader = + Thread.currentThread().getContextClassLoader(); + if (classLoader != oldCtxClassLoader) { + Thread.currentThread().setContextClassLoader(classLoader); + } + try { + if (filterClass == null) { + filterClass = (Class) classLoader.loadClass(filterClassName); + } + this.filter = (Filter) filterClass.newInstance(); + } finally { + if (classLoader != oldCtxClassLoader) { + Thread.currentThread().setContextClassLoader(oldCtxClassLoader); + } + } + + // TODO: resource injection + + return filter; + } + + public Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException { + Filter filter = createFilter(); + filter.init(this); + return (this.filter); + } + + + /** + * Release the Filter instance associated with this FilterConfig, + * if there is one. + */ + public void release() { + if (this.filter != null){ + filter.destroy(); + } + this.filter = null; + } + + + @Override + public void addMappingForServletNames(EnumSet dispatcherTypes, + boolean isMatchAfter, + String... servletNames) { + if (ctx.startDone) { + // Use the context method instead of the servlet API to + // add mappings after context init. + throw new IllegalStateException(); + } + ArrayList dispatchers = new ArrayList(); + for (DispatcherType dt: dispatcherTypes) { + dispatchers.add(dt.name()); + } + for (String servletName: servletNames) { + ctx.getFilterMapper().addMapping(getFilterName(), + null, servletName, (String[]) dispatchers.toArray(), isMatchAfter); + } + } + + + @Override + public void addMappingForUrlPatterns(EnumSet dispatcherTypes, + boolean isMatchAfter, + String... urlPatterns) { + if (ctx.startDone) { + // Use the context method instead of the servlet API to + // add mappings after context init. + throw new IllegalStateException(); + } + ArrayList dispatchers = new ArrayList(); + for (DispatcherType dt: dispatcherTypes) { + dispatchers.add(dt.name()); + } + for (String url: urlPatterns) { + ctx.getFilterMapper().addMapping(getFilterName(), + url, null, (String[]) dispatchers.toArray(), isMatchAfter); + } + } + + + @Override + public boolean setInitParameter(String name, String value) + throws IllegalArgumentException, IllegalStateException { + return ServletContextImpl.setInitParameter(ctx, initParams, + name, value); + } + + + @Override + public Set setInitParameters(Map initParameters) + throws IllegalArgumentException, IllegalStateException { + return ServletContextImpl.setInitParameters(ctx, initParams, + initParameters); + } + + public Dynamic getDynamic() { + return dynamic; + } + + public class DynamicFilterRegistration implements Dynamic { + + + @Override + public void addMappingForServletNames(EnumSet dispatcherTypes, + boolean isMatchAfter, + String... servletNames) { + FilterConfigImpl.this.addMappingForServletNames(dispatcherTypes, isMatchAfter, servletNames); + } + + + @Override + public void addMappingForUrlPatterns(EnumSet dispatcherTypes, + boolean isMatchAfter, + String... urlPatterns) { + FilterConfigImpl.this.addMappingForUrlPatterns(dispatcherTypes, isMatchAfter, urlPatterns); + } + + + @Override + public boolean setInitParameter(String name, String value) + throws IllegalArgumentException, IllegalStateException { + return ServletContextImpl.setInitParameter(ctx, initParams, + name, value); + } + + + @Override + public Set setInitParameters(Map initParameters) + throws IllegalArgumentException, IllegalStateException { + return ServletContextImpl.setInitParameters(ctx, initParams, + initParameters); + } + + + @Override + public void setAsyncSupported(boolean isAsyncSupported) + throws IllegalStateException { + asyncSupported = isAsyncSupported; + } + + + public void setDescription(String description) + throws IllegalStateException { + FilterConfigImpl.this.descryption = description; + } + + @Override + public Collection getUrlPatternMappings() { + // implement me + return null; + } + + @Override + public Collection getServletNameMappings() { + // implement me + return null; + } + + @Override + public Map getInitParameters() { + // implement me + return null; + } + + @Override + public String getInitParameter(String name) { + if (initParams == null) return null; + return initParams.get(name); + } + + @Override + public String getClassName() { + // implement me + return null; + } + + @Override + public String getName() { + // implement me + return null; + } + } + + @Override + public Collection getUrlPatternMappings() { + // implement me + return null; + } + + @Override + public Collection getServletNameMappings() { + // implement me + return null; + } + + @Override + public Map getInitParameters() { + // implement me + return null; + } + + @Override + public String getClassName() { + // implement me + return null; + } + + @Override + public String getName() { + // implement me + return null; + } + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/JspLoader.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/JspLoader.java new file mode 100644 index 000000000..d07644da6 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/JspLoader.java @@ -0,0 +1,31 @@ +/* + * 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.servlet; + +import javax.servlet.ServletContext; + +import org.apache.tomcat.servlets.jsp.BaseJspLoader; + +public class JspLoader extends BaseJspLoader { + + public ClassLoader getClassLoader(ServletContext ctx) { + return ((ServletContextImpl) ctx).getClassLoader(); + } + + public String getClassPath(ServletContext ctx) { + return ((ServletContextImpl) ctx).getClassPath(); + } +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/Locale2Charset.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/Locale2Charset.java new file mode 100644 index 000000000..84a644c8a --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/Locale2Charset.java @@ -0,0 +1,128 @@ +/* + * 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.servlet; + + +import java.io.InputStream; +import java.util.Locale; +import java.util.Properties; + + + +/** + * One instance per Context. Holds the + * + * Utility class that attempts to map from a Locale to the corresponding + * character set to be used for interpreting input text (or generating + * output text) when the Content-Type header does not include one. You + * can customize the behavior of this class by modifying the mapping data + * it loads, or by subclassing it (to change the algorithm) and then using + * your own version for a particular web application. + * + * @author Craig R. McClanahan + */ +public class Locale2Charset { + + + // ---------------------------------------------------- Manifest Constants + + + /** + * Default properties resource name. + */ + public static final String DEFAULT_RESOURCE = + "/org/apache/coyote/servlet/CharsetMapperDefault.properties"; + + + + // ---------------------------------------------------------- Constructors + + + /** + * Construct a new CharsetMapper using the default properties resource. + */ + public Locale2Charset() { + String name = DEFAULT_RESOURCE; + if (defaultMap == null) { // once ! + try { + defaultMap = new Properties(); + InputStream stream = + this.getClass().getResourceAsStream(name); + defaultMap.load(stream); + stream.close(); + } catch (Throwable t) { + throw new IllegalArgumentException(t.toString()); + } + } + map = defaultMap; + } + + + // ---------------------------------------------------- Instance Variables + + + private static Properties defaultMap; // shared for all apps + + /** + * The mapping properties that have been initialized from the specified or + * default properties resource. + */ + private Properties map; + + + // ------------------------------------------------------- Public Methods + + + /** + * Calculate the name of a character set to be assumed, given the specified + * Locale and the absence of a character set specified as part of the + * content type header. + * + * @param locale The locale for which to calculate a character set + */ + public String getCharset(Locale locale) { + // Match full language_country_variant first, then language_country, + // then language only + String charset = map.getProperty(locale.toString()); + if (charset == null) { + charset = map.getProperty(locale.getLanguage() + "_" + + locale.getCountry()); + if (charset == null) { + charset = map.getProperty(locale.getLanguage()); + } + } + return (charset); + } + + + /** + * The deployment descriptor can have a + * locale-encoding-mapping-list element which describes the + * webapp's desired mapping from locale to charset. This method + * gets called when processing the web.xml file for a context + * + * @param locale The locale for a character set + * @param charset The charset to be associated with the locale + */ + public void addCharsetMapping(String locale, String charset) { + if (map == defaultMap) { + // new copy, don't modify original + map = new Properties(defaultMap); + } + map.put(locale, charset); + } +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ParameterMap.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ParameterMap.java new file mode 100644 index 000000000..eb05317b7 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ParameterMap.java @@ -0,0 +1,204 @@ +/* + * 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.servlet; + + +import java.util.HashMap; +import java.util.Map; + + +/** + * Extended implementation of HashMap that includes a + * locked property. This class can be used to safely expose + * Catalina internal parameter map objects to user classes without having + * to clone them in order to avoid modifications. When first created, a + * ParmaeterMap instance is not locked. + * + * @author Craig R. McClanahan + * @version $Revision$ $Date$ + */ + +public final class ParameterMap extends HashMap { + + + // ----------------------------------------------------------- Constructors + + + /** + * Construct a new, empty map with the default initial capacity and + * load factor. + */ + public ParameterMap() { + + super(); + + } + + + /** + * Construct a new, empty map with the specified initial capacity and + * default load factor. + * + * @param initialCapacity The initial capacity of this map + */ + public ParameterMap(int initialCapacity) { + + super(initialCapacity); + + } + + + /** + * Construct a new, empty map with the specified initial capacity and + * load factor. + * + * @param initialCapacity The initial capacity of this map + * @param loadFactor The load factor of this map + */ + public ParameterMap(int initialCapacity, float loadFactor) { + + super(initialCapacity, loadFactor); + + } + + + /** + * Construct a new map with the same mappings as the given map. + * + * @param map Map whose contents are dupliated in the new map + */ + public ParameterMap(Map map) { + + super(map); + + } + + + // ------------------------------------------------------------- Properties + + + /** + * The current lock state of this parameter map. + */ + private boolean locked = false; + + + /** + * Return the locked state of this parameter map. + */ + public boolean isLocked() { + + return (this.locked); + + } + + + /** + * Set the locked state of this parameter map. + * + * @param locked The new locked state + */ + public void setLocked(boolean locked) { + + this.locked = locked; + + } + + + // --------------------------------------------------------- Public Methods + + + + /** + * Remove all mappings from this map. + * + * @exception IllegalStateException if this map is currently locked + */ + public void clear() { + + if (locked) + throw new IllegalStateException + ("parameterMap.locked"); + super.clear(); + + } + + + /** + * Associate the specified value with the specified key in this map. If + * the map previously contained a mapping for this key, the old value is + * replaced. + * + * @param key Key with which the specified value is to be associated + * @param value Value to be associated with the specified key + * + * @return The previous value associated with the specified key, or + * null if there was no mapping for key + * + * @exception IllegalStateException if this map is currently locked + */ + public Object put(Object key, Object value) { + + if (locked) + throw new IllegalStateException + ("parameterMap.locked"); + return (super.put(key, value)); + + } + + + /** + * Copy all of the mappings from the specified map to this one. These + * mappings replace any mappings that this map had for any of the keys + * currently in the specified Map. + * + * @param map Mappings to be stored into this map + * + * @exception IllegalStateException if this map is currently locked + */ + public void putAll(Map map) { + + if (locked) + throw new IllegalStateException + ("parameterMap.locked"); + super.putAll(map); + + } + + + /** + * Remove the mapping for this key from the map if present. + * + * @param key Key whose mapping is to be removed from the map + * + * @return The previous value associated with the specified key, or + * null if there was no mapping for that key + * + * @exception IllegalStateException if this map is currently locked + */ + public Object remove(Object key) { + + if (locked) + throw new IllegalStateException + ("parameterMap.locked"); + return (super.remove(key)); + + } + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/RequestDispatcherImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/RequestDispatcherImpl.java new file mode 100644 index 000000000..fbbbfef0e --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/RequestDispatcherImpl.java @@ -0,0 +1,853 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletRequestWrapper; +import javax.servlet.ServletResponse; +import javax.servlet.ServletResponseWrapper; +import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.mapper.MappingData; + +/** + * + */ +public final class RequestDispatcherImpl implements RequestDispatcher { + /** + * The request attribute under which the original servlet path is stored + * on an forwarded dispatcher request. + */ + public static final String FORWARD_SERVLET_PATH_ATTR = + "javax.servlet.forward.servlet_path"; + + + /** + * The request attribute under which the original query string is stored + * on an forwarded dispatcher request. + */ + public static final String FORWARD_QUERY_STRING_ATTR = + "javax.servlet.forward.query_string"; + + /** + * The request attribute under which the original request URI is stored + * on an forwarded dispatcher request. + */ + public static final String FORWARD_REQUEST_URI_ATTR = + "javax.servlet.forward.request_uri"; + + + /** + * The request attribute under which the original context path is stored + * on an forwarded dispatcher request. + */ + public static final String FORWARD_CONTEXT_PATH_ATTR = + "javax.servlet.forward.context_path"; + + + /** + * The request attribute under which the original path info is stored + * on an forwarded dispatcher request. + */ + public static final String FORWARD_PATH_INFO_ATTR = + "javax.servlet.forward.path_info"; + + /** + * The request attribute under which we store the servlet name on a + * named dispatcher request. + */ + public static final String NAMED_DISPATCHER_ATTR = + "org.apache.catalina.NAMED"; + + /** + * The request attribute under which the request URI of the included + * servlet is stored on an included dispatcher request. + */ + public static final String INCLUDE_REQUEST_URI_ATTR = + "javax.servlet.include.request_uri"; + + + /** + * The request attribute under which the context path of the included + * servlet is stored on an included dispatcher request. + */ + public static final String INCLUDE_CONTEXT_PATH_ATTR = + "javax.servlet.include.context_path"; + + + /** + * The request attribute under which the path info of the included + * servlet is stored on an included dispatcher request. + */ + public static final String INCLUDE_PATH_INFO_ATTR = + "javax.servlet.include.path_info"; + + + /** + * The request attribute under which the servlet path of the included + * servlet is stored on an included dispatcher request. + */ + public static final String INCLUDE_SERVLET_PATH_ATTR = + "javax.servlet.include.servlet_path"; + + + /** + * The request attribute under which the query string of the included + * servlet is stored on an included dispatcher request. + */ + public static final String INCLUDE_QUERY_STRING_ATTR = + "javax.servlet.include.query_string"; + + /** + * The request attribute under which we expose the value of the + * <jsp-file> value associated with this servlet, + * if any. + */ + public static final String JSP_FILE_ATTR = + "org.apache.catalina.jsp_file"; + + + // ----------------------------------------------------- Instance Variables + + private static Logger log = Logger.getLogger(RequestDispatcherImpl.class.getName()); + + private ServletContextImpl ctx = null; + + /** + * The servlet name for a named dispatcher. + */ + private String name = null; + + // Path for a path dispatcher + private String path; + + /** + * MappingData object - per thread for buffering. + */ + private transient ThreadLocal localMappingData = new ThreadLocal(); + + /* + OrigRequest(ServletRequestImpl) -> include/forward * -> this include + + On the path: user-defined RequestWrapper or our ServletRequestWrapper + + include() is called with a RequestWrapper(->...->origRequest) or origRequest + + Based on params, etc -> we wrap the req / response in ServletRequestWrapper, + call filters+servlet. Inside, the req can be wrapped again in + userReqWrapper, and other include called. + + + */ + + /** + * The outermost request that will be passed on to the invoked servlet. + */ + private ServletRequest outerRequest = null; + + /** + * The outermost response that will be passed on to the invoked servlet. + */ + private ServletResponse outerResponse = null; + + /** + * The request wrapper we have created and installed (if any). + */ + private ServletRequest wrapRequest = null; + + /** + * The response wrapper we have created and installed (if any). + */ + private ServletResponse wrapResponse = null; + + // Parameters used when constructing the dispatcvher + /** + * The extra path information for this RequestDispatcher. + */ + private String pathInfo = null; + /** + * The query string parameters for this RequestDispatcher. + */ + private String queryString = null; + /** + * The request URI for this RequestDispatcher. + */ + private String requestURI = null; + /** + * The servlet path for this RequestDispatcher. + */ + private String servletPath = null; + + // + private String origServletPath = null; + + /** + * The Wrapper associated with the resource that will be forwarded to + * or included. + */ + private ServletConfigImpl wrapper = null; + + private Servlet servlet; + + /** Named dispatcher + */ + public RequestDispatcherImpl(ServletConfigImpl wrapper, String name) { + this.wrapper = wrapper; + this.name = name; + this.ctx = (ServletContextImpl) wrapper.getServletContext(); + + } + + public RequestDispatcherImpl(ServletContextImpl ctx, String path) { + this.path = path; + this.ctx = ctx; + } + + + + /** + * Forward this request and response to another resource for processing. + * Any runtime exception, IOException, or ServletException thrown by the + * called servlet will be propogated to the caller. + * + * @param request The servlet request to be forwarded + * @param response The servlet response to be forwarded + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet exception occurs + */ + public void forward(ServletRequest request, ServletResponse response) + throws ServletException, IOException + { + // Reset any output that has been buffered, but keep headers/cookies + if (response.isCommitted()) { + throw new IllegalStateException("forward(): response.isComitted()"); + } + + try { + response.resetBuffer(); + } catch (IllegalStateException e) { + throw e; + } + + // Set up to handle the specified request and response + setup(request, response, false); + + // Identify the HTTP-specific request and response objects (if any) + HttpServletRequest hrequest = (HttpServletRequest) request; + + ServletRequestWrapperImpl wrequest = + (ServletRequestWrapperImpl) wrapRequest(); + + + if (name != null) { + wrequest.setRequestURI(hrequest.getRequestURI()); + wrequest.setContextPath(hrequest.getContextPath()); + wrequest.setServletPath(hrequest.getServletPath()); + wrequest.setPathInfo(hrequest.getPathInfo()); + wrequest.setQueryString(hrequest.getQueryString()); + + + } else { // path based + mapPath(); + if (wrapper == null) { + throw new ServletException("Forward not found " + + path); + } + String contextPath = ctx.getContextPath(); + if (hrequest.getAttribute(FORWARD_REQUEST_URI_ATTR) == null) { + wrequest.setAttribute(FORWARD_REQUEST_URI_ATTR, + hrequest.getRequestURI()); + wrequest.setAttribute(FORWARD_CONTEXT_PATH_ATTR, + hrequest.getContextPath()); + wrequest.setAttribute(FORWARD_SERVLET_PATH_ATTR, + hrequest.getServletPath()); + wrequest.setAttribute(FORWARD_PATH_INFO_ATTR, + hrequest.getPathInfo()); + wrequest.setAttribute(FORWARD_QUERY_STRING_ATTR, + hrequest.getQueryString()); + } + + wrequest.setContextPath(contextPath); + wrequest.setRequestURI(requestURI); + wrequest.setServletPath(servletPath); + wrequest.setPathInfo(pathInfo); + if (queryString != null) { + wrequest.setQueryString(queryString); + wrequest.setQueryParams(queryString); + } + } + processRequest(outerRequest, outerResponse); + + wrequest.recycle(); + unwrapRequest(); + + // This is not a real close in order to support error processing +// if ( log.isDebugEnabled() ) +// log.debug(" Disabling the response for futher output"); + + if (response instanceof ServletResponseImpl) { + ((ServletResponseImpl) response).flushBuffer(); + ((ServletResponseImpl) response).setSuspended(true); + } else { + // Servlet SRV.6.2.2. The Resquest/Response may have been wrapped + // and may no longer be instance of RequestFacade + if (log.isLoggable(Level.FINE)){ + log.fine( " The Response is vehiculed using a wrapper: " + + response.getClass().getName() ); + } + + // Close anyway + try { + PrintWriter writer = response.getWriter(); + writer.close(); + } catch (IllegalStateException e) { + try { + ServletOutputStream stream = response.getOutputStream(); + stream.close(); + } catch (IllegalStateException f) { + ; + } catch (IOException f) { + ; + } + } catch (IOException e) { + ; + } + } + } + + + + /** + * Include the response from another resource in the current response. + * Any runtime exception, IOException, or ServletException thrown by the + * called servlet will be propogated to the caller. + * + * @param request The servlet request that is including this one + * @param response The servlet response to be appended to + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet exception occurs + */ + public void include(ServletRequest request, ServletResponse response) + throws ServletException, IOException + { + + // Set up to handle the specified request and response + setup(request, response, true); + + // Create a wrapped response to use for this request + // this actually gets inserted somewhere in the chain - it's not + // the last one, but first non-user response + wrapResponse(); + ServletRequestWrapperImpl wrequest = + (ServletRequestWrapperImpl) wrapRequest(); + + + // Handle an HTTP named dispatcher include + if (name != null) { + wrequest.setAttribute(NAMED_DISPATCHER_ATTR, name); + if (servletPath != null) wrequest.setServletPath(servletPath); + wrequest.setAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR, + new Integer(WebappFilterMapper.INCLUDE)); + wrequest.setAttribute(WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR, + origServletPath); + } else { + mapPath(); + String contextPath = ctx.getContextPath(); + if (requestURI != null) + wrequest.setAttribute(INCLUDE_REQUEST_URI_ATTR, + requestURI); + if (contextPath != null) + wrequest.setAttribute(INCLUDE_CONTEXT_PATH_ATTR, + contextPath); + if (servletPath != null) + wrequest.setAttribute(INCLUDE_SERVLET_PATH_ATTR, + servletPath); + if (pathInfo != null) + wrequest.setAttribute(INCLUDE_PATH_INFO_ATTR, + pathInfo); + if (queryString != null) { + wrequest.setAttribute(INCLUDE_QUERY_STRING_ATTR, + queryString); + wrequest.setQueryParams(queryString); + } + + wrequest.setAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR, + new Integer(WebappFilterMapper.INCLUDE)); + wrequest.setAttribute(WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR, + origServletPath); + } + + invoke(outerRequest, outerResponse); + + wrequest.recycle(); + unwrapRequest(); + unwrapResponse(); + } + + + // -------------------------------------------------------- Private Methods + + public void mapPath() { + if (path == null || servletPath != null) return; + + // Retrieve the thread local URI, used for mapping + // TODO: recycle RequestDispatcher stack and associated objects + // instead of this object + + // Retrieve the thread local mapping data + MappingData mappingData = (MappingData) localMappingData.get(); + if (mappingData == null) { + mappingData = new MappingData(); + localMappingData.set(mappingData); + } + + // Get query string + int pos = path.indexOf('?'); + if (pos >= 0) { + queryString = path.substring(pos + 1); + } else { + pos = path.length(); + } + + // Map the URI + MessageBytes uriMB = MessageBytes.newInstance(); + CharChunk charBuffer = new CharChunk(); + //mappingData.localURIBytes; + uriMB.recycle(); + //CharChunk uriCC = uriMB.getCharChunk(); + try { + /* + * Ignore any trailing path params (separated by ';') for mapping + * purposes. + * This is sometimes broken - path params can be on any path + * component, not just last. + */ + int semicolon = path.indexOf(';'); + if (pos >= 0 && semicolon > pos) { + semicolon = -1; + } + if (ctx.getContextPath().length() > 1 ) { + charBuffer.append(ctx.getContextPath()); + } + charBuffer.append(path, 0, + semicolon > 0 ? semicolon : pos); + + // Wrap the buffer + uriMB.setChars(charBuffer.getBuffer(), + charBuffer.getOffset(), + charBuffer.getLength()); + + // TODO: make charBuffer part of request or something + ctx.getMapper().map(uriMB, mappingData); + + // at least default wrapper must be returned + + /* + * Append any trailing path params (separated by ';') that were + * ignored for mapping purposes, so that they're reflected in the + * RequestDispatcher's requestURI + */ + if (semicolon > 0) { + // I don't think this will be used in future + charBuffer.append(path, + semicolon, pos - semicolon); + } + } catch (Exception e) { + log.log(Level.SEVERE, "getRequestDispatcher()", e); + } + + wrapper = (ServletConfigImpl) mappingData.wrapper; + servletPath = mappingData.wrapperPath.toString(); + pathInfo = mappingData.pathInfo.toString(); + + mappingData.recycle(); + + } + + + /** + * Prepare the request based on the filter configuration. + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet error occurs + */ + private void processRequest(ServletRequest request, + ServletResponse response) + throws IOException, ServletException { + Integer disInt = + (Integer) request.getAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR); + if (disInt != null) { + if (disInt.intValue() != WebappFilterMapper.ERROR) { + outerRequest.setAttribute + (WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR, + origServletPath); + outerRequest.setAttribute + (WebappFilterMapper.DISPATCHER_TYPE_ATTR, + new Integer(WebappFilterMapper.FORWARD)); + } + invoke(outerRequest, response); + } + + } + + + + + /** + * Ask the resource represented by this RequestDispatcher to process + * the associated request, and create (or append to) the associated + * response. + *

+ * IMPLEMENTATION NOTE: This implementation assumes + * that no filters are applied to a forwarded or included resource, + * because they were already done for the original request. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet error occurs + */ + private void invoke(ServletRequest request, ServletResponse response) + throws IOException, ServletException { + + // Checking to see if the context classloader is the current context + // classloader. If it's not, we're saving it, and setting the context + // classloader to the Context classloader + ClassLoader oldCCL = Thread.currentThread().getContextClassLoader(); + ClassLoader contextClassLoader = ctx.getClassLoader(); + + if (oldCCL != contextClassLoader) { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } else { + oldCCL = null; + } + + // Initialize local variables we may need + HttpServletResponse hresponse = (HttpServletResponse) response; + IOException ioException = null; + ServletException servletException = null; + RuntimeException runtimeException = null; + + servletException = allocateServlet(hresponse, servletException); + + // Get the FilterChain Here + WebappFilterMapper factory = + ((ServletContextImpl)wrapper.getServletContext()).getFilterMapper(); + + FilterChainImpl filterChain = factory.createFilterChain(request, + wrapper, + servlet); + + // Call the service() method for the allocated servlet instance + try { + String jspFile = wrapper.getJspFile(); + if (jspFile != null) + request.setAttribute(JSP_FILE_ATTR, jspFile); + else + request.removeAttribute(JSP_FILE_ATTR); + // for includes/forwards + if ((servlet != null) && (filterChain != null)) { + filterChain.doFilter(request, response); + } + } catch (IOException e) { + ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " + + wrapper.getServletName(), e); + ioException = e; + } catch (UnavailableException e) { + ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " + + wrapper.getServletName(), e); + servletException = e; + wrapper.unavailable(e); + } catch (ServletException e) { + servletException = e; + } catch (RuntimeException e) { + ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " + + wrapper.getServletName(), e); + runtimeException = e; + } + request.removeAttribute(JSP_FILE_ATTR); + + // Release the filter chain (if any) for this request + if (filterChain != null) + filterChain.release(); + + servletException = servletDealocate(servletException); + + // Reset the old context class loader + if (oldCCL != null) + Thread.currentThread().setContextClassLoader(oldCCL); + + // Unwrap request/response if needed + unwrapRequest(); + unwrapResponse(); + + // Rethrow an exception if one was thrown by the invoked servlet + if (ioException != null) + throw ioException; + if (servletException != null) + throw servletException; + if (runtimeException != null) + throw runtimeException; + + } + + private ServletException servletDealocate(ServletException servletException) + { + if (servlet != null) { + wrapper.deallocate(servlet); + } + return servletException; + } + + private ServletException allocateServlet(HttpServletResponse hresponse, + ServletException servletException) + throws IOException + { + boolean unavailable = false; + + // Check for the servlet being marked unavailable + if (wrapper.isUnavailable()) { + ctx.getLogger().log(Level.WARNING, "isUnavailable() " + wrapper.getServletName()); + long available = wrapper.getAvailable(); + if ((available > 0L) && (available < Long.MAX_VALUE)) + hresponse.setDateHeader("Retry-After", available); + hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, + "Unavailable"); // No need to include internal info: wrapper.getServletName(); + unavailable = true; + } + + // Allocate a servlet instance to process this request + try { + if (!unavailable) { + servlet = wrapper.allocate(); + } + } catch (ServletException e) { + ctx.getLogger().log(Level.WARNING, "RequestDispatcher: allocate " + + wrapper.toString()); + servletException = e; + servlet = null; + } catch (Throwable e) { + ctx.getLogger().log(Level.WARNING, "allocate() error " + wrapper.getServletName(), e); + servletException = new ServletException + ("Allocate error " + wrapper.getServletName(), e); + servlet = null; + } + return servletException; + } + + + /** + * Set up to handle the specified request and response + * + * @param request The servlet request specified by the caller + * @param response The servlet response specified by the caller + * @param including Are we performing an include() as opposed to + * a forward()? + */ + private void setup(ServletRequest request, ServletResponse response, + boolean including) { + + this.outerRequest = request; + this.outerResponse = response; + } + + + /** + * Unwrap the request if we have wrapped it. Not sure how it could end + * up in the middle. + */ + private void unwrapRequest() { + if (wrapRequest == null) + return; + + ServletRequest previous = null; + ServletRequest current = outerRequest; + while (current != null) { + // If we run into the container request we are done + if (current instanceof ServletRequestImpl) + break; + + // Remove the current request if it is our wrapper + if (current == wrapRequest) { + ServletRequest next = + ((ServletRequestWrapper) current).getRequest(); + if (previous == null) + outerRequest = next; + else + ((ServletRequestWrapper) previous).setRequest(next); + break; + } + + // Advance to the next request in the chain + previous = current; + current = ((ServletRequestWrapper) current).getRequest(); + } + } + + + /** + * Unwrap the response if we have wrapped it. + */ + private void unwrapResponse() { + if (wrapResponse == null) + return; + + ServletResponse previous = null; + ServletResponse current = outerResponse; + while (current != null) { + // If we run into the container response we are done + if (current instanceof ServletResponseImpl) + break; + + // Remove the current response if it is our wrapper + if (current == wrapResponse) { + ServletResponse next = + ((ServletResponseWrapper) current).getResponse(); + if (previous == null) + outerResponse = next; + else + ((ServletResponseWrapper) previous).setResponse(next); + break; + } + // Advance to the next response in the chain + previous = current; + current = ((ServletResponseWrapper) current).getResponse(); + } + } + + + /** + * Create and return a request wrapper that has been inserted in the + * appropriate spot in the request chain. + */ + private ServletRequest wrapRequest() { + // Locate the request we should insert in front of + ServletRequest previous = null; + ServletRequest current = outerRequest; + while (current != null) { + if (!(current instanceof ServletRequestWrapper)) + break; + if (current instanceof ServletRequestWrapperImpl) + break; + if (current instanceof ServletRequestImpl) + break; + // user-specified + previous = current; + current = ((ServletRequestWrapper) current).getRequest(); + } + // now previous will be a user-specified wrapper, + // and current one of our own wrappers ( deeper in stack ) + // ... current USER_previous USER USER + // previous is null if the top request is ours. + + // Instantiate a new wrapper at this point and insert it in the chain + ServletRequest wrapper = null; + + // Compute a crossContext flag + boolean crossContext = isCrossContext(); + wrapper = + new ServletRequestWrapperImpl((HttpServletRequest) current, + ctx, crossContext); + + if (previous == null) { + // outer becomes the wrapper, includes orig wrapper inside + outerRequest = wrapper; + } else { + // outer remains user-specified sersvlet, delegating to + // our wrapper, which delegates to real request or our wrapper. + ((ServletRequestWrapper) previous).setRequest(wrapper); + } + wrapRequest = wrapper; + return (wrapper); + } + + private boolean isCrossContext() { + boolean crossContext = false; + if ((outerRequest instanceof ServletRequestWrapperImpl) || + (outerRequest instanceof ServletRequestImpl) || + (outerRequest instanceof HttpServletRequest)) { + HttpServletRequest houterRequest = + (HttpServletRequest) outerRequest; + Object contextPath = + houterRequest.getAttribute(INCLUDE_CONTEXT_PATH_ATTR); + if (contextPath == null) { + // Forward + contextPath = houterRequest.getContextPath(); + } + crossContext = !(ctx.getContextPath().equals(contextPath)); + } + return crossContext; + } + + + /** + * Create and return a response wrapper that has been inserted in the + * appropriate spot in the response chain. + * + * Side effect: updates outerResponse, wrapResponse. + * The chain is updated with a wrapper below lowest user wrapper + */ + private ServletResponse wrapResponse() { + // Locate the response we should insert in front of + ServletResponse previous = null; + ServletResponse current = outerResponse; + while (current != null) { + if (!(current instanceof ServletResponseWrapper)) + break; + if (current instanceof ServletResponseImpl) + break; + previous = current; + current = ((ServletResponseWrapper) current).getResponse(); + } + + // Instantiate a new wrapper at this point and insert it in the chain + ServletResponse wrapper = + new ServletResponseIncludeWrapper(current); + + if (previous == null) { + // outer is ours, we can wrap on top + outerResponse = wrapper; + } else { + // outer is user-specified, leave it alone. + // we insert ourself below the lowest user-specified response + ((ServletResponseWrapper) previous).setResponse(wrapper); + } + wrapResponse = wrapper; + return (wrapper); + + } + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletConfigImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletConfigImpl.java new file mode 100644 index 000000000..c8ef7ca6d --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletConfigImpl.java @@ -0,0 +1,982 @@ +/* + * Copyright 1999-2002,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.servlet; + +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.MultipartConfigElement; +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.ServletSecurityElement; +import javax.servlet.SingleThreadModel; +import javax.servlet.UnavailableException; + +import org.apache.tomcat.servlets.jsp.BaseJspLoader; +import org.apache.tomcat.servlets.util.Enumerator; +import org.apache.tomcat.util.IntrospectionUtils; + +/** + * Based on Wrapper. + * + * Standard implementation of the Wrapper interface that represents + * an individual servlet definition. No child Containers are allowed, and + * the parent Container must be a Context. + * + * @author Craig R. McClanahan + * @author Remy Maucherat + */ +@SuppressWarnings("deprecation") +public class ServletConfigImpl implements ServletConfig, ServletRegistration { + + ServletDynamicRegistration dynamic = new ServletDynamicRegistration(); + + protected boolean asyncSupported; + + private static Logger log= + Logger.getLogger(ServletConfigImpl.class.getName()); + + private static final String[] DEFAULT_SERVLET_METHODS = new String[] { + "GET", "HEAD", "POST" }; + + // TODO: refactor all 'stm' to separate class (not implemented) + // public static final String SINGLE_THREADED_PROXY = + // "org.apache.tomcat.servlets.jsp.SingleThreadedProxyServlet"; + + protected String description; + protected Map initParams = new HashMap(); + protected String servletName; + protected String servletClassName; + protected String jspFile; + protected int loadOnStartup = -1; + protected String runAs; + protected Map securityRoleRef = new HashMap(); // roleName -> [roleLink] + + /** + * The date and time at which this servlet will become available (in + * milliseconds since the epoch), or zero if the servlet is available. + * If this value equals Long.MAX_VALUE, the unavailability of this + * servlet is considered permanent. + */ + private transient long available = 0L; + + private ServletContextImpl ctx; + + /** + * The (single) initialized instance of this servlet. + */ + private transient Servlet instance = null; + + /** + * Are we unloading our servlet instance at the moment? + */ + private transient boolean unloading = false; + + private Class servletClass = null; + + // Support for SingleThreaded + /** + * The count of allocations that are currently active (even if they + * are for the same instance, as will be true on a non-STM servlet). + */ + private transient int countAllocated = 0; + + private transient boolean singleThreadModel = false; + /** + * Stack containing the STM instances. + */ + private transient Stack instancePool = null; + + + // Statistics + private transient long loadTime=0; + private transient int classLoadTime=0; + + // ------------------------------------------------------------- Properties + public ServletConfigImpl(ServletContextImpl ctx, String name, + String classname) { + this.servletName = name; + this.servletClassName = classname; + this.ctx = ctx; + ctx.facade.notifyAdd(this); + } + + /** + * Return the available date/time for this servlet, in milliseconds since + * the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean + * that unavailability is permanent and any request for this servlet will return + * an SC_NOT_FOUND error. If this date/time is in the future, any request for + * this servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero, + * the servlet is currently available. + */ + public long getAvailable() { + return (this.available); + } + + + /** + * Set the available date/time for this servlet, in milliseconds since the + * epoch. If this date/time is Long.MAX_VALUE, it is considered to mean + * that unavailability is permanent and any request for this servlet will return + * an SC_NOT_FOUND error. If this date/time is in the future, any request for + * this servlet will return an SC_SERVICE_UNAVAILABLE error. + * + * @param available The new available date/time + */ + public void setAvailable(long available) { + + long oldAvailable = this.available; + if (available > System.currentTimeMillis()) + this.available = available; + else + this.available = 0L; + + } + + + /** + * Return the number of active allocations of this servlet, even if they + * are all for the same instance (as will be true for servlets that do + * not implement SingleThreadModel. + */ + public int getCountAllocated() { + return (this.countAllocated); + } + + /** + * Return the jsp-file setting for this servlet. + */ + public String getJspFile() { + return jspFile; + } + + public void setJspFile(String s) { + this.jspFile = s; + } + + /** + * Return the load-on-startup order value (negative value means + * load on first call). + */ + public int getLoadOnStartup() { + return loadOnStartup; + } + + /** + * Return the fully qualified servlet class name for this servlet. + */ + public String getServletClass() { + return servletClassName; + } + + /** + * Is this servlet currently unavailable? + */ + public boolean isUnavailable() { + if (available == 0L) + return (false); + else if (available <= System.currentTimeMillis()) { + available = 0L; + return (false); + } else + return (true); + + } + + + /** + * Gets the names of the methods supported by the underlying servlet. + * + * This is the same set of methods included in the Allow response header + * in response to an OPTIONS request method processed by the underlying + * servlet. + * + * @return Array of names of the methods supported by the underlying + * servlet + */ + public String[] getServletMethods() throws ServletException { + + Class servletClazz = loadServlet().getClass(); + if (!javax.servlet.http.HttpServlet.class.isAssignableFrom( + servletClazz)) { + return DEFAULT_SERVLET_METHODS; + } + + HashSet allow = new HashSet(); + allow.add("TRACE"); + allow.add("OPTIONS"); + + Method[] methods = getAllDeclaredMethods(servletClazz); + for (int i=0; methods != null && inull + * to mark this servlet as permanently unavailable + */ + public void unavailable(UnavailableException unavailable) { + getServletContext().log("UnavailableException:" + getServletName()); + if (unavailable == null) + setAvailable(Long.MAX_VALUE); + else if (unavailable.isPermanent()) + setAvailable(Long.MAX_VALUE); + else { + int unavailableSeconds = unavailable.getUnavailableSeconds(); + if (unavailableSeconds <= 0) + unavailableSeconds = 60; // Arbitrary default + setAvailable(System.currentTimeMillis() + + (unavailableSeconds * 1000L)); + } + + } + + + /** + * Unload all initialized instances of this servlet, after calling the + * destroy() method for each instance. This can be used, + * for example, prior to shutting down the entire servlet engine, or + * prior to reloading all of the classes from the Loader associated with + * our Loader's repository. + * + * @exception ServletException if an exception is thrown by the + * destroy() method + */ + public synchronized void unload() throws ServletException { + setAvailable(Long.MAX_VALUE); + + // Nothing to do if we have never loaded the instance + if (!singleThreadModel && (instance == null)) + return; + unloading = true; + + // Loaf a while if the current instance is allocated + // (possibly more than once if non-STM) + if (countAllocated > 0) { + int nRetries = 0; + long delay = ctx.getUnloadDelay() / 20; + while ((nRetries < 21) && (countAllocated > 0)) { + if ((nRetries % 10) == 0) { + log.info("Servlet.unload() timeout " + + countAllocated); + } + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + ; + } + nRetries++; + } + } + + ClassLoader oldCtxClassLoader = + Thread.currentThread().getContextClassLoader(); + if (instance != null) { + ClassLoader classLoader = instance.getClass().getClassLoader(); + + PrintStream out = System.out; + // Call the servlet destroy() method + try { + Thread.currentThread().setContextClassLoader(classLoader); + instance.destroy(); + } catch (Throwable t) { + instance = null; + //instancePool = null; + unloading = false; + throw new ServletException("Servlet.destroy() " + + getServletName(), t); + } finally { + // restore the context ClassLoader + Thread.currentThread().setContextClassLoader(oldCtxClassLoader); + } + + // Deregister the destroyed instance + instance = null; + } + if (singleThreadModel && (instancePool != null)) { + try { + ClassLoader classLoader = ctx.getClassLoader(); + Thread.currentThread().setContextClassLoader(classLoader); + while (!instancePool.isEmpty()) { + ((Servlet) instancePool.pop()).destroy(); + } + } catch (Throwable t) { + instancePool = null; + unloading = false; + throw new ServletException("Servlet.destroy() " + getServletName(), t); + } finally { + // restore the context ClassLoader + Thread.currentThread().setContextClassLoader + (oldCtxClassLoader); + } + instancePool = null; + } + + singleThreadModel = false; + + unloading = false; + } + + + /** + * Return the initialization parameter value for the specified name, + * if any; otherwise return null. + * + * @param name Name of the initialization parameter to retrieve + */ + public String getInitParameter(String name) { + return initParams.get(name); + } + + + /** + * Return the set of initialization parameter names defined for this + * servlet. If none are defined, an empty Enumeration is returned. + */ + public Enumeration getInitParameterNames() { + synchronized (initParams) { + return (new Enumerator(initParams.keySet())); + } + } + + + /** + * Return the servlet context with which this servlet is associated. + */ + public ServletContext getServletContext() { + return ctx; + } + + + /** + * Return the name of this servlet. + */ + public String getServletName() { + return servletName; + } + +// public long getProcessingTime() { +// return swValve.getProcessingTime(); +// } +// +// public void setProcessingTime(long processingTime) { +// swValve.setProcessingTime(processingTime); +// } +// +// public long getMaxTime() { +// return swValve.getMaxTime(); +// } +// +// public void setMaxTime(long maxTime) { +// swValve.setMaxTime(maxTime); +// } +// +// public long getMinTime() { +// return swValve.getMinTime(); +// } +// +// public void setMinTime(long minTime) { +// swValve.setMinTime(minTime); +// } +// +// public int getRequestCount() { +// return swValve.getRequestCount(); +// } +// +// public void setRequestCount(int requestCount) { +// swValve.setRequestCount(requestCount); +// } +// +// public int getErrorCount() { +// return swValve.getErrorCount(); +// } +// +// public void setErrorCount(int errorCount) { +// swValve.setErrorCount(errorCount); +// } +// +// /** +// * Increment the error count used for monitoring. +// */ +// public void incrementErrorCount(){ +// swValve.setErrorCount(swValve.getErrorCount() + 1); +// } +// +// public long getLoadTime() { +// return loadTime; +// } +// +// public void setLoadTime(long loadTime) { +// this.loadTime = loadTime; +// } +// +// public int getClassLoadTime() { +// return classLoadTime; +// } + + // -------------------------------------------------------- Package Methods + + + // -------------------------------------------------------- Private Methods + + private Method[] getAllDeclaredMethods(Class c) { + + if (c.equals(javax.servlet.http.HttpServlet.class)) { + return null; + } + + Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass()); + + Method[] thisMethods = c.getDeclaredMethods(); + if (thisMethods == null) { + return parentMethods; + } + + if ((parentMethods != null) && (parentMethods.length > 0)) { + Method[] allMethods = + new Method[parentMethods.length + thisMethods.length]; + System.arraycopy(parentMethods, 0, allMethods, 0, + parentMethods.length); + System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, + thisMethods.length); + + thisMethods = allMethods; + } + + return thisMethods; + } + + /** Specify the instance. Avoids the class lookup, disables unloading. + * Use for embedded case, or to control the allocation. + * + * @param servlet + */ + public void setServlet(Servlet servlet) { + instance = servlet; + ctx.getObjectManager().bind("Servlet:" + + ctx.getContextPath() + ":" + getServletName(), + this); + } + + public String getSecurityRoleRef(String role) { + return (String)securityRoleRef.get(role); + } + + public void setSecurityRoleRef(Map securityRoles) { + this.securityRoleRef = securityRoles; + } + + public void setConfig(Map initParams) { + this.initParams = initParams; + } + + public void setLoadOnStartup(int loadOnStartup) { + this.loadOnStartup = loadOnStartup; + } + + @Override + public Set addMapping(String... urlPatterns) { + if (ctx.startDone) { + // Use the context method instead of the servlet API to + // add mappings after context init. + throw new IllegalStateException(); + } + Set failed = new HashSet(); + for (String url: urlPatterns) { + if (url == null) { + throw new IllegalArgumentException(); + } + if (ctx.contextConfig.servletMapping.get(url) != null) { + failed.add(url); + } else { + ctx.contextConfig.servletMapping.put(url, getServletName()); + ctx.addMapping(url, this); + } + } + return failed; + } + + + @Override + public boolean setInitParameter(String name, String value) + throws IllegalArgumentException, IllegalStateException { + return ServletContextImpl.setInitParameter(ctx, initParams, + name, value); + } + + + @Override + public Set setInitParameters(Map initParameters) + throws IllegalArgumentException, IllegalStateException { + return ServletContextImpl.setInitParameters(ctx, initParams, + initParameters); + } + + public Dynamic getDynamic() { + return dynamic; + } + + class ServletDynamicRegistration implements Dynamic { + + + @Override + public void setAsyncSupported(boolean isAsyncSupported) + throws IllegalStateException { + asyncSupported = isAsyncSupported; + } + + + public void setDescription(String description) + throws IllegalStateException { + ServletConfigImpl.this.description = description; + } + + @Override + public Set setServletSecurity(ServletSecurityElement constraint) { + //implement me + return null; + } + + @Override + public void setLoadOnStartup(int loadOnStartup) { + //implement me - here to compile + } + + @Override + public void setMultipartConfig(MultipartConfigElement multipartConfig) { + //implement me - here to compile + } + + @Override + public void setRunAsRole(String roleName) { + //implement me - here to compile + } + + @Override + public String getRunAsRole() { + //implement me - here to compile + return null; + } + + @Override + public Collection getMappings() { + //implement me + return null; + } + + @Override + public Set addMapping(String... urlPatterns) { + //implement me + return null; + } + + @Override + public Map getInitParameters() { + // implement me + return null; + } + + @Override + public Set setInitParameters(Map initParameters) + throws IllegalArgumentException, IllegalStateException { + // implement me + return null; + } + + @Override + public String getClassName() { + // implement me + return null; + } + + @Override + public String getName() { + // implement me + return null; + } + + @Override + public String getInitParameter(String name) { + // implement me + return null; + } + + @Override + public boolean setInitParameter(String name, String value) + throws IllegalArgumentException, IllegalStateException { + // implement me + return false; + } + + } + + @Override + public Collection getMappings() { + //implement me + return null; + } + + public void setServletClass(Class servletClass2) { + servletClass = servletClass2; + } + + + @Override + public String getRunAsRole() { + //implement me + return null; + } + + + @Override + public Map getInitParameters() { + // implement me + return null; + } + + @Override + public String getClassName() { + // implement me + return null; + } + + @Override + public String getName() { + // implement me + return null; + } + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletContextImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletContextImpl.java new file mode 100644 index 000000000..85a5883de --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletContextImpl.java @@ -0,0 +1,1699 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.naming.NamingException; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterRegistration; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletContextAttributeEvent; +import javax.servlet.ServletContextAttributeListener; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import javax.servlet.FilterRegistration.Dynamic; +import javax.servlet.descriptor.JspConfigDescriptor; + +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.servlets.session.UserSessionManager; +import org.apache.tomcat.integration.ObjectManager; +import org.apache.tomcat.servlets.config.ConfigLoader; +import org.apache.tomcat.servlets.config.ServletContextConfig; +import org.apache.tomcat.servlets.config.ServletContextConfig.FilterData; +import org.apache.tomcat.servlets.config.ServletContextConfig.FilterMappingData; +import org.apache.tomcat.servlets.config.ServletContextConfig.ServletData; +import org.apache.tomcat.servlets.config.deploy.WarDeploy; +import org.apache.tomcat.servlets.util.Enumerator; +import org.apache.tomcat.servlets.util.RequestUtil; +import org.apache.tomcat.servlets.util.UrlUtils; +import org.apache.tomcat.util.http.MimeMap; + + +/** + * Context - initialized from web.xml or using APIs. + * + * Initialization order: + * + * - add all listeners + * - add all filters + * - add all servlets + * + * - session parameters + * - + * + * @author Craig R. McClanahan + * @author Remy Maucherat + * @version $Revision$ $Date$ + */ + +public class ServletContextImpl implements ServletContext { + + /** + * Empty collection to serve as the basis for empty enumerations. + */ + private transient static final ArrayList empty = new ArrayList(); + + transient Logger log; + + /** + * Base path - the directory root of the webapp + */ + protected String basePath = null; + + protected String contextPath; + + // All config from web.xml + protected ServletContextConfig contextConfig = new ServletContextConfig(); + + MimeMap contentTypes = new MimeMap(); + + /** + * The context attributes for this context. + */ + protected transient Map attributes = new HashMap(); + + /** + * List of read only attributes for this context. + * In catalina - used to protect workdir att. We trust the app, so no need + * for extra complexity. + */ + //protected transient HashMap readOnlyAttributes = new HashMap(); + + protected transient ArrayList lifecycleListeners = new ArrayList(); + + protected UserSessionManager manager; + + HashMap filters = new HashMap(); + + HashMap servlets = new HashMap(); + + ArrayList securityRoles = new ArrayList(); + + /** Mapper for filters. + */ + protected WebappFilterMapper webappFilterMapper; + + /** Internal mapper for request dispatcher, must have all + * context mappings. + */ + protected WebappServletMapper mapper; + + transient Locale2Charset charsetMapper = new Locale2Charset(); + + transient TomcatLite facade; + + ObjectManager om; + + private String hostname; + + + boolean initDone = false; + + boolean startDone = false; + + String jspcServlet = "org.apache.tomcat.servlets.jspc.JspcServlet"; + + + // ------------------------------------------------- ServletContext Methods + public ServletContextImpl() { + } + + public void setTomcat(TomcatLite facade) { + this.facade = facade; + } + + /** + * Registry/framework interface associated with the context. + * Also available as a context attribute. + * @return + */ + public ObjectManager getObjectManager() { + if (om == null) { + om = facade.getObjectManager(); + } + return om; + } + + public void setObjectManager(ObjectManager om) { + this.om = om; + } + + public Locale2Charset getCharsetMapper() { + return charsetMapper; + } + + /** + * Set the context path, starting with "/" - "/" for ROOT + * @param path + */ + public void setContextPath(String path) { + this.contextPath = path; + log = Logger.getLogger("webapp" + path.replace('/', '.')); + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public String getHostname() { + return hostname; + } + + /** The directory where this app is based. May be null. + * + * @param basePath + */ + public void setBasePath(String basePath) { + this.basePath = basePath; + } + + public ServletContextConfig getContextConfig() { + return contextConfig; + } + + /** The directory where this app is based. + * + * @param basePath + */ + public String getBasePath() { + return basePath; + } + + public String getEncodedPath() { + return null; + } + + + public boolean getCookies() { + return false; + } + + + public ServletContext getServletContext() { + return this; + } + + public List getListeners() { + return lifecycleListeners; + } + + public void addListener(Class listenerClass) { + // implement me + } + + public void addListener(String className) { + // implement me + } + + public void addListener(T t) { + lifecycleListeners.add(t); + } + + + public void removeListener(EventListener listener) { + lifecycleListeners.remove(listener); + } + + + + public Logger getLogger() { + return log; + } + + public long getUnloadDelay() { + return 0; + } + + public ServletConfigImpl getServletConfig(String jsp_servlet_name) { + return (ServletConfigImpl)servlets.get(jsp_servlet_name); + } + + public Map getServletConfigs() { + return servlets; + } + + /** + * Add a servlet to the context. + * Called from processWebAppData() + * + * @param servletConfig + */ + public void addServletConfig(ServletConfigImpl servletConfig) { + servlets.put(servletConfig.getServletName(), servletConfig); + } + + public boolean getPrivileged() { + return false; + } + + + public Map getFilters() { + return filters; + } + + + protected boolean getCrossContext() { + return true; + } + + public void addMimeType(String ext, String type) { + contentTypes.addContentType(ext, type); + } + + public WebappServletMapper getMapper() { + if (mapper == null) { + Object customMapper = getObjectManager().get(WebappServletMapper.class); + if (customMapper == null) { + mapper = new WebappServletMapper(); + } else { + mapper = (WebappServletMapper) customMapper; + } + mapper.setServletContext(this); + } + + return mapper; + } + + public WebappFilterMapper getFilterMapper() { + if (webappFilterMapper == null) { + Object customMapper = getObjectManager().get(WebappFilterMapper.class); + if (customMapper == null) { + webappFilterMapper = new WebappFilterMapper(); + } else { + webappFilterMapper = (WebappFilterMapper) customMapper; + } + webappFilterMapper.setServletContext(this); + } + + return webappFilterMapper ; + } + + public FilterConfigImpl getFilter(String name) { + return (FilterConfigImpl)filters.get(name); + } + + /** + * Return the value of the specified context attribute, if any; + * otherwise return null. + * + * @param name Name of the context attribute to return + */ + public Object getAttribute(String name) { + if ("ObjectManager".equals(name)) { + return om; + } + if ("context-listeners".equals(name)) { + return lifecycleListeners; + } + return (attributes.get(name)); + } + + /** + * Return an enumeration of the names of the context attributes + * associated with this context. + */ + public Enumeration getAttributeNames() { + return new Enumerator(attributes.keySet(), true); + } + + + public void addSecurityRole(String role) { + securityRoles.add(role); + } + + public List getSecurityRoles() { + return securityRoles; + } + + /** + * Return a ServletContext object that corresponds to a + * specified URI on the server. This method allows servlets to gain + * access to the context for various parts of the server, and as needed + * obtain RequestDispatcher objects or resources from the + * context. The given path must be absolute (beginning with a "/"), + * and is interpreted based on our virtual host's document root. + * + * @param uri Absolute URI of a resource on the server + */ + public ServletContext getContext(String uri) { + // TODO: support real uri ( http://host/path ) + // Validate the format of the specified argument + if ((uri == null) || (!uri.startsWith("/"))) + return (null); + + ServletContextImpl child = null; + try { + child = facade.getContext(this, uri); + } catch (IOException e) { + } catch (ServletException e) { + } + + if (child == null) + return (null); + + if (this.getCrossContext()) { + // If crossContext is enabled, can always return the context + return child.getServletContext(); + } else if (child == this) { + // Can still return the current context + return this.getServletContext(); + } else { + // Nothing to return + return (null); + } + } + + + /** + * Return the main path associated with this context. + */ + public String getContextPath() { + return contextPath; + } + + + /** + * Return the value of the specified initialization parameter, or + * null if this parameter does not exist. + * + * @param name Name of the initialization parameter to retrieve + */ + public String getInitParameter(final String name) { + return ((String) contextConfig.contextParam.get(name)); + } + + + /** + * Return the names of the context's initialization parameters, or an + * empty enumeration if the context has no initialization parameters. + */ + public Enumeration getInitParameterNames() { + return (new Enumerator(contextConfig.contextParam.keySet())); + } + + public void setContextParams(Map newParams) { + contextConfig.contextParam = (HashMap) newParams; + } + + /** + * Return the major version of the Java Servlet API that we implement. + */ + public int getMajorVersion() { + return 3; + } + + + /** + * Return the minor version of the Java Servlet API that we implement. + */ + public int getMinorVersion() { + return 0; + } + + + /** + * Return the MIME type of the specified file, or null if + * the MIME type cannot be determined. + * + * @param file Filename for which to identify a MIME type + */ + public String getMimeType(String file) { + return contentTypes.getMimeType(file); + } + + /** + * Return the real path for a given virtual path, if possible; otherwise + * return null. + * + * @param path The path to the desired resource + */ + public String getRealPath(String path) { + if (path == null) { + return null; + } + + File file = new File(basePath, path); + return (file.getAbsolutePath()); + } + + /** + * Return a RequestDispatcher object that acts as a + * wrapper for the named servlet. + * + * @param name Name of the servlet for which a dispatcher is requested + */ + public RequestDispatcher getNamedDispatcher(String name) { + if (name == null) return null; + ServletConfigImpl wrapper = + (ServletConfigImpl) this.getServletConfig(name); + if (wrapper == null) return null; + + return new RequestDispatcherImpl(wrapper, name); + } + + + /** + * Return a RequestDispatcher instance that acts as a + * wrapper for the resource at the given path. The path must begin + * with a "/" and is interpreted as relative to the current context root. + * + * @param path The path to the desired resource. + */ + public RequestDispatcher getRequestDispatcher(String path) { + if (path == null) return null; + + if (!path.startsWith("/")) + throw new IllegalArgumentException(path); + + path = UrlUtils.normalize(path); + if (path == null) return (null); + + + return new RequestDispatcherImpl(this, path); + } + + public RequestDispatcher getRequestDispatcher(String path, + int type, + String dispatcherPath) { + RequestDispatcher dispatcher = getRequestDispatcher(path); + //((RequestDispatcherImpl)dispatcher); + return dispatcher; + } + + ThreadLocal requestDispatcherStack = new ThreadLocal(); + + private String classPath; + + protected ClassLoader classLoader; + +// protected RequestDispatcherImpl getRequestDispatcher() { +// ArrayList/**/ list = +// (ArrayList)requestDispatcherStack.get(); +// if (list == null) { +// list = new ArrayList(); +// requestDispatcherStack.set(list); +// } +// +// +// return null; +// } + + public void resetDispatcherStack() { + + } + + /** + * Return the URL to the resource that is mapped to a specified path. + * The path must begin with a "/" and is interpreted as relative to the + * current context root. + * + * @param path The path to the desired resource + * + * @exception MalformedURLException if the path is not given + * in the correct form + */ + public URL getResource(String path) + throws MalformedURLException { + + if (path == null || !path.startsWith("/")) { + throw new MalformedURLException("getResource() " + path); + } + + path = UrlUtils.normalize(path); + if (path == null) + return (null); + + String libPath = "/WEB-INF/lib/"; + if ((path.startsWith(libPath)) && (path.endsWith(".jar"))) { + File jarFile = null; + jarFile = new File(basePath, path); + if (jarFile.exists()) { + return jarFile.toURL(); + } else { + return null; + } + } else { + File resFile = new File(basePath + path); + if (resFile.exists()) { + return resFile.toURL(); + } + } + + return (null); + + } + + /** + * Return the requested resource as an InputStream. The + * path must be specified according to the rules described under + * getResource. If no such resource can be identified, + * return null. + * + * @param path The path to the desired resource. + */ + public InputStream getResourceAsStream(String path) { + + path = UrlUtils.normalize(path); + if (path == null) + return (null); + + File resFile = new File(basePath + path); + if (!resFile.exists()) + return null; + + try { + return new FileInputStream(resFile); + } catch (FileNotFoundException e) { + return null; + } + + } + + + /** + * Return a Set containing the resource paths of resources member of the + * specified collection. Each path will be a String starting with + * a "/" character. The returned set is immutable. + * + * @param path Collection path + */ + public Set getResourcePaths(String path) { + + // Validate the path argument + if (path == null) { + return null; + } + if (!path.startsWith("/")) { + throw new IllegalArgumentException("getResourcePaths() " + path); + } + + path = UrlUtils.normalize(path); + if (path == null) + return (null); + + File f = new File(basePath + path); + File[] files = f.listFiles(); + if (files == null) return null; + if (!path.endsWith("/")) { + path = path + "/"; + } + + HashSet result = new HashSet(); + for (int i=0; i < files.length; i++) { + if (files[i].isDirectory() ) { + result.add(path + files[i].getName() + "/"); + } else { + result.add(path + files[i].getName()); + } + } + return result; + } + + + + /** + * Return the name and version of the servlet container. + */ + public String getServerInfo() { + return "Apache Tomcat Lite"; + } + + /** + * @deprecated As of Java Servlet API 2.1, with no direct replacement. + */ + public Servlet getServlet(String name) { + return (null); + } + + + /** + * Return the display name of this web application. + */ + public String getServletContextName() { + return contextConfig.displayName; + } + + + /** + * @deprecated As of Java Servlet API 2.1, with no direct replacement. + */ + public Enumeration getServletNames() { + return (new Enumerator(empty)); + } + + + /** + * @deprecated As of Java Servlet API 2.1, with no direct replacement. + */ + public Enumeration getServlets() { + return (new Enumerator(empty)); + } + + + /** + * Writes the specified message to a servlet log file. + * + * @param message Message to be written + */ + public void log(String message) { + this.getLogger().info(message); + } + + + /** + * Writes the specified exception and message to a servlet log file. + * + * @param exception Exception to be reported + * @param message Message to be written + * + * @deprecated As of Java Servlet API 2.1, use + * log(String, Throwable) instead + */ + public void log(Exception exception, String message) { + this.getLogger().log(Level.INFO, message, exception); + } + + + /** + * Writes the specified message and exception to a servlet log file. + * + * @param message Message to be written + * @param throwable Exception to be reported + */ + public void log(String message, Throwable throwable) { + this.getLogger().log(Level.INFO, message, throwable); + } + + /** + * Remove the context attribute with the specified name, if any. + * + * @param name Name of the context attribute to be removed + */ + public void removeAttribute(String name) { + + Object value = null; + boolean found = false; + + // Remove the specified attribute + // Check for read only attribute + found = attributes.containsKey(name); + if (found) { + value = attributes.get(name); + attributes.remove(name); + } else { + return; + } + + // Notify interested application event listeners + List listeners = this.getListeners(); + if (listeners.size() == 0) + return; + ServletContextAttributeEvent event = null; + for (int i = 0; i < listeners.size(); i++) { + if (!(listeners.get(i) instanceof ServletContextAttributeListener)) + continue; + ServletContextAttributeListener listener = + (ServletContextAttributeListener) listeners.get(i); + try { + if (event == null) { + event = new ServletContextAttributeEvent(this.getServletContext(), + name, value); + + } + listener.attributeRemoved(event); + } catch (Throwable t) { + // FIXME - should we do anything besides log these? + log("ServletContextAttributeListener", t); + } + } + } + + + /** + * Bind the specified value with the specified context attribute name, + * replacing any existing value for that name. + * + * @param name Attribute name to be bound + * @param value New attribute value to be bound + */ + public void setAttribute(String name, Object value) { + // Name cannot be null + if (name == null) + throw new IllegalArgumentException + ("name == null"); + + // Null value is the same as removeAttribute() + if (value == null) { + removeAttribute(name); + return; + } + + Object oldValue = null; + boolean replaced = false; + + // Add or replace the specified attribute + synchronized (attributes) { + // Check for read only attribute + oldValue = attributes.get(name); + if (oldValue != null) + replaced = true; + attributes.put(name, value); + } + + // Notify interested application event listeners + List listeners = this.getListeners(); + if (listeners.size() == 0) + return; + ServletContextAttributeEvent event = null; + for (int i = 0; i < listeners.size(); i++) { + if (!(listeners.get(i) instanceof ServletContextAttributeListener)) + continue; + ServletContextAttributeListener listener = + (ServletContextAttributeListener) listeners.get(i); + try { + if (event == null) { + if (replaced) + event = + new ServletContextAttributeEvent(this.getServletContext(), + name, oldValue); + else + event = + new ServletContextAttributeEvent(this.getServletContext(), + name, value); + + } + if (replaced) { + listener.attributeReplaced(event); + } else { + listener.attributeAdded(event); + } + } catch (Throwable t) { + // FIXME - should we do anything besides log these? + log("ServletContextAttributeListener error", t); + } + } + + } + + /** + * Clear all application-created attributes. + */ + void clearAttributes() { + // Create list of attributes to be removed + ArrayList list = new ArrayList(); + synchronized (attributes) { + Iterator iter = attributes.keySet().iterator(); + while (iter.hasNext()) { + list.add(iter.next()); + } + } + + // Remove application originated attributes + // (read only attributes will be left in place) + Iterator keys = list.iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + removeAttribute(key); + } + } + + public void initFilters() throws ServletException { + Iterator fI = getFilters().values().iterator(); + while (fI.hasNext()) { + FilterConfigImpl fc = (FilterConfigImpl)fI.next(); + try { + fc.getFilter(); // will triger init() + } catch (Throwable e) { + log.log(Level.WARNING, getContextPath() + " Filter.init() " + + fc.getFilterName(), e); + } + + } + } + + public void initServlets() throws ServletException { + Iterator fI = getServletConfigs().values().iterator(); + Map/*>*/ onStartup = + new TreeMap/*>*/(); + while (fI.hasNext()) { + ServletConfigImpl fc = (ServletConfigImpl)fI.next(); + if (fc.getLoadOnStartup() > 0 ) { + Integer i = new Integer(fc.getLoadOnStartup()); + List/**/ old = (List)onStartup.get(i); + if (old == null) { + old = new ArrayList/**/(); + onStartup.put(i, old); + } + old.add(fc); + } + } + Iterator keys = onStartup.keySet().iterator(); + while (keys.hasNext()) { + Integer key = (Integer)keys.next(); + List/**/ servlets = (List)onStartup.get(key); + Iterator servletsI = servlets.iterator(); + while (servletsI.hasNext()) { + ServletConfigImpl fc = (ServletConfigImpl) servletsI.next(); + try { + fc.loadServlet(); + } catch (Throwable e) { + log.log(Level.WARNING, "Error initializing " + fc.getServletName(), e); + } + } + } + } + + public void initListeners() throws ServletException { + Iterator fI = contextConfig.listenerClass.iterator(); + while (fI.hasNext()) { + String listenerClass = (String)fI.next(); + try { + Object l = + getClassLoader().loadClass(listenerClass).newInstance(); + lifecycleListeners.add((EventListener) l); + } catch (Throwable e) { + log.log(Level.WARNING, "Error initializing listener " + listenerClass, e); + } + } + } + + public ClassLoader getClassLoader() { + return classLoader; + } + + public void addMapping(String path, String name) { + ServletConfigImpl wrapper = getServletConfig(name); + addMapping(path, wrapper); + } + + public void addMapping(String path, ServletConfig wrapper) { + getMapper().addWrapper(getMapper().contextMapElement, path, wrapper); + } + + + + public void setWelcomeFiles(String[] name) { + getMapper().contextMapElement.welcomeResources = name; + } + + public String[] getWelcomeFiles() { + return getMapper().contextMapElement.welcomeResources; + } + + public void setSessionTimeout(int to) { + getManager().setSessionTimeout(to); + } + + /** + * Initialize the context from the parsed config. + * + * Note that WebAppData is serializable. + */ + public void processWebAppData(ServletContextConfig d) throws ServletException { + this.contextConfig = d; + + for (String k: d.mimeMapping.keySet()) { + addMimeType(k, d.mimeMapping.get(k)); + } + + String[] wFiles = (String[])d.welcomeFileList.toArray(new String[0]); + if (wFiles.length == 0) { + wFiles = new String[] {"index.html" }; + } + if (basePath != null) { + getMapper().contextMapElement.resources = new File(getBasePath()); + } + setWelcomeFiles(wFiles); + + Iterator i2 = d.filters.values().iterator(); + while (i2.hasNext()) { + FilterData fd = (FilterData)i2.next(); + addFilter(fd.name, fd.className, fd.initParams); + } + + Iterator i3 = d.servlets.values().iterator(); + while (i3.hasNext()) { + ServletData sd = (ServletData) i3.next(); + // jsp-file + if (sd.className == null) { + if (sd.jspFile == null) { + log.log(Level.WARNING, "Missing servlet class for " + sd.name); + continue; + } + } + + ServletConfigImpl sw = + new ServletConfigImpl(this, sd.name, sd.className); + sw.setConfig(sd.initParams); + sw.setJspFile(sd.jspFile); + sw.setLoadOnStartup(sd.loadOnStartup); + //sw.setRunAs(sd.runAs); + sw.setSecurityRoleRef(sd.securityRoleRef); + + addServletConfig(sw); + } + + for (String k: d.servletMapping.keySet()) { + addMapping(k, d.servletMapping.get(k)); + } + + Iterator i5 = d.filterMappings.iterator(); + while (i5.hasNext()) { + FilterMappingData k = (FilterMappingData) i5.next(); + String[] disp = new String[k.dispatcher.size()]; + if (k.urlPattern != null) { + addFilterMapping(k.urlPattern, + k.filterName, + (String[])k.dispatcher.toArray(disp)); + } + if (k.servletName != null) { + addFilterServletMapping(k.servletName, + k.filterName, + (String[])k.dispatcher.toArray(disp)); + } + } + + for (String n: d.localeEncodingMapping.keySet()) { + getCharsetMapper().addCharsetMapping(n, + d.localeEncodingMapping.get(n)); + } + } + + public void addServlet(String servletName, String servletClass, + String jspFile, Map params) { + ServletConfigImpl sc = new ServletConfigImpl(this, servletName, + servletClass); + sc.setJspFile(jspFile); + sc.setConfig(params); + addServletConfig(sc); + } + + @Override + public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { + ServletConfigImpl sc = new ServletConfigImpl(this, servletName, null); + sc.setServlet(servlet); + addServletConfig(sc); + return sc.getDynamic(); + } + + public void addServletSec(String serlvetName, String runAs, Map roles) { + // TODO + } + + + + public void addFilterMapping(String path, String filterName, + String[] dispatcher) { + getFilterMapper().addMapping(filterName, + path, null, dispatcher, true); + + } + + public void addFilterServletMapping(String servlet, + String filterName, + String[] dispatcher) { + getFilterMapper().addMapping(filterName, + null, servlet, + dispatcher, true); + } + + /** + * Called from TomcatLite.init(), required before start. + * + * Will initialize defaults and load web.xml unless webAppData is + * already set and recent. No other processing is done except reading + * the config - you can add or alter it before start() is called. + * + * @throws ServletException + */ + public void init() throws ServletException { + if (initDone) { + return; + } + initDone = true; + // Load global init params from the facade + initEngineDefaults(); + + initTempDir(); + + + // Merge in web.xml - or other config source ( programmatic, etc ) + ConfigLoader cfg = new WarDeploy(); + contextConfig = cfg.loadConfig(getBasePath()); + + processWebAppData(contextConfig); + + // if not defined yet: + addDefaultServlets(); + } + + + protected void initTempDir() throws ServletException { + // We need a base path - at least for temp files, req. by spec + if (basePath == null) { + basePath = ("/".equals(contextPath)) ? + facade.getWork().getAbsolutePath() + "/ROOT" : + facade.getWork().getAbsolutePath() + contextPath; + } + + File f = new File(basePath + "/WEB-INF/tmp"); + f.mkdirs(); + setAttribute("javax.servlet.context.tempdir", f); + } + + /** + * Static file handler ( default ) + * *.jsp support + * + */ + protected void addDefaultServlets() throws ServletException { + if (servlets.get("default") == null) { + ServletConfigImpl fileS = new ServletConfigImpl(this, + "default", null); + addServletConfig(fileS); + addMapping("/", fileS); + } + + // *.jsp support + if (servlets.get("jspwildcard") == null) { + ServletConfigImpl fileS = new ServletConfigImpl(this, + "jspwildcard", null); + fileS.initParams.put("mapper", JspLoader.class.getName()); + addServletConfig(fileS); + addMapping("*.jsp", fileS); + } + + ServletConfigImpl jspcS = new ServletConfigImpl(this, + "jspc", jspcServlet); + addServletConfig(jspcS); + } + + protected void initEngineDefaults() throws ServletException { + + // TODO: make this customizable, avoid loading it on startup + // Set the class name as default in the addon support + for (String sname: facade.ctxDefaultInitParam.keySet()) { + String path = facade.ctxDefaultInitParam.get(sname); + contextConfig.contextParam.put(sname, path); + } + + for (String sname: facade.preloadServlets.keySet()) { + String sclass = facade.preloadServlets.get(sname); + ServletConfigImpl fileS = new ServletConfigImpl(this, sname, sclass); + addServletConfig(fileS); + } + + for (String sname: facade.preloadMappings.keySet()) { + String path = facade.preloadMappings.get(sname); + ServletConfigImpl servletConfig = getServletConfig(sname); + addMapping(path, servletConfig); + } + + // JSP support + setAttribute(InstanceManager.class.getName(), + new LiteInstanceManager(getObjectManager())); + + } + + private void addClasspathLib(ArrayList res, File directory) { + + if (!directory.isDirectory() || !directory.exists() + || !directory.canRead()) { + return; + } + + File[] jars = directory.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + }); + + for (int j = 0; j < jars.length; j++) { + try { + URL url = jars[j].toURL(); + res.add(url); + } catch (MalformedURLException e) { + } + } + } + + private void addClasspathDir(ArrayList res, File classesDir) { + + if (classesDir.isDirectory() && classesDir.exists() && + classesDir.canRead()) { + try { + URL url = classesDir.toURL(); + res.add(url); + } catch (MalformedURLException e) { + } + } + } + public String getClassPath() { + return classPath; + } + + public void start() throws ServletException { + if (startDone) { + return; + } + String base = getBasePath(); + + ArrayList urls = new ArrayList(); + + addClasspathDir(urls, new File(base + "/WEB-INF/classes")); + addClasspathDir(urls, new File(base + "/WEB-INF/tmp")); + addClasspathLib(urls, new File(base + "/WEB-INF/lib")); + + URL[] urlsA = new URL[urls.size()]; + urls.toArray(urlsA); + + URLClassLoader parentLoader = + getEngine().getContextParentLoader(); + + // create a class loader. + // TODO: reimplement special 'deploy' dirs + + /* + Repository ctxRepo = new Repository(); + ctxRepo.setParentClassLoader(parentLoader); + ctxRepo.addURL(urlsA); + repository = ctxRepo; + */ + + StringBuilder cp = new StringBuilder(); + + for (URL cpUrl : urlsA) { + cp.append(":").append(cpUrl.getFile()); + } + classPath = cp.toString(); + + classLoader = new URLClassLoader(urlsA, parentLoader); + + // JMX should know about us ( TODO: is it too early ? ) + facade.notifyAdd(this); + + initListeners(); + + List listeners = this.getListeners(); + ServletContextEvent event = null; + for (int i = 0; i < listeners.size(); i++) { + if (!(listeners.get(i) instanceof ServletContextListener)) + continue; + ServletContextListener listener = + (ServletContextListener) listeners.get(i); + if (event == null) { + event = new ServletContextEvent(this); + } + try { + // May add servlets/filters + listener.contextInitialized(event); + } catch (Throwable t) { + log.log(Level.WARNING, "Context.init() contextInitialized() error:", t); + } + } + + + initFilters(); + initServlets(); + + startDone = true; + } + + public UserSessionManager getManager() { + if (manager == null) { + manager = (UserSessionManager) getObjectManager().get( + UserSessionManager.class); + manager.setContext(this); + if (contextConfig.sessionTimeout > 0 ) { + manager.setSessionTimeout(contextConfig.sessionTimeout); + } + } + return manager; + } + + + // TODO: configurable ? init-params + public String getSessionCookieName() { + return "JSESSIONID"; + } + + + + public void destroy() throws ServletException { + // destroy filters + Iterator fI = filters.values().iterator(); + while(fI.hasNext()) { + FilterConfigImpl fc = (FilterConfigImpl) fI.next(); + try { + fc.getFilter().destroy(); + } catch (Exception e) { + e.printStackTrace(); + } + } + // destroy servlets + fI = servlets.values().iterator(); + while(fI.hasNext()) { + ServletConfigImpl fc = (ServletConfigImpl) fI.next(); + try { + fc.unload(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public TomcatLite getEngine() { + return facade; + } + + public String findStatusPage(int status) { + if (contextConfig.errorPageCode.size() == 0) { + return null; + } + if (status == 200) { + return null; + } + + return (String) contextConfig.errorPageCode.get(Integer.toString(status)); + } + + public void handleStatusPage(ServletRequestImpl req, + ServletResponseImpl res, + int status, + String statusPage) { + String message = RequestUtil.filter(res.getMessage()); + if (message == null) + message = ""; + setErrorAttributes(req, status, message); + dispatchError(req, res, statusPage); + } + + protected void setErrorAttributes(ServletRequestImpl req, + int status, + String message) { + req.setAttribute("javax.servlet.error.status_code", + new Integer(status)); + if (req.getWrapper() != null) { + req.setAttribute("javax.servlet.error.servlet_name", + req.getWrapper().servletName); + } + req.setAttribute("javax.servlet.error.request_uri", + req.getRequestURI()); + req.setAttribute("javax.servlet.error.message", + message); + + } + + public void handleError(ServletRequestImpl req, + ServletResponseImpl res, + Throwable t) { + Throwable realError = t; + if (realError instanceof ServletException) { + realError = ((ServletException) realError).getRootCause(); + if (realError == null) { + realError = t; + } + } + //if (realError instanceof ClientAbortException ) { + + String errorPage = findErrorPage(t); + if ((errorPage == null) && (realError != t)) { + errorPage = findErrorPage(realError); + } + + if (errorPage != null) { + setErrorAttributes(req, 500, t.getMessage()); + req.setAttribute("javax.servlet.error.exception", realError); + req.setAttribute("javax.servlet.error.exception_type", + realError.getClass()); + dispatchError(req, res, errorPage); + } else { + log("Unhandled error", t); + if (t instanceof ServletException && + ((ServletException)t).getRootCause() != null) { + log("RootCause:", ((ServletException)t).getRootCause()); + } + if (res.getStatus() < 500) { + res.setStatus(500); + } + } + } + + protected void dispatchError(ServletRequestImpl req, + ServletResponseImpl res, + String errorPage) { + RequestDispatcher rd = + getRequestDispatcher(errorPage); + try { + // will clean up the buffer + rd.forward(req, res); + return; // handled + } catch (ServletException e) { + // TODO + } catch (IOException e) { + // TODO + } + } + + protected String findErrorPage(Throwable exception) { + if (contextConfig.errorPageException.size() == 0) { + return null; + } + if (exception == null) + return (null); + Class clazz = exception.getClass(); + String name = clazz.getName(); + while (!Object.class.equals(clazz)) { + String page = (String)contextConfig.errorPageException.get(name); + if (page != null) + return (page); + clazz = clazz.getSuperclass(); + if (clazz == null) + break; + name = clazz.getName(); + } + return (null); + + } + + + @Override + public EnumSet getDefaultSessionTrackingModes() { + return null; + } + + + @Override + public EnumSet getEffectiveSessionTrackingModes() { + return null; + } + + + @Override + public SessionCookieConfig getSessionCookieConfig() { + return null; + } + + + @Override + public void setSessionTrackingModes(EnumSet sessionTrackingModes) { + } + + + public void addFilter(String filterName, String filterClass, + Map params) { + FilterConfigImpl fc = new FilterConfigImpl(this); + fc.setData(filterName, filterClass, params); + filters.put(filterName, fc); + } + + + @Override + public Dynamic addFilter(String filterName, String className) { + FilterConfigImpl fc = new FilterConfigImpl(this); + fc.setData(filterName, className, new HashMap()); + filters.put(filterName, fc); + return fc.getDynamic(); + } + + + @Override + public Dynamic addFilter(String filterName, Filter filter) { + FilterConfigImpl fc = new FilterConfigImpl(this); + fc.setData(filterName, null, new HashMap()); + fc.setFilter(filter); + filters.put(filterName, fc); + return fc.getDynamic(); + } + + + @Override + public Dynamic addFilter(String filterName, Class filterClass) { + FilterConfigImpl fc = new FilterConfigImpl(this); + fc.setData(filterName, null, new HashMap()); + fc.setFilterClass(filterClass); + filters.put(filterName, fc); + return fc.getDynamic(); + } + + + @Override + public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, + String className) { + ServletConfigImpl sc = new ServletConfigImpl(this, servletName, className); + addServletConfig(sc); + return sc.getDynamic(); + } + + + @Override + public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, + Class servletClass) { + ServletConfigImpl sc = new ServletConfigImpl(this, servletName, servletClass.getName()); + sc.setServletClass(servletClass); + addServletConfig(sc); + return sc.getDynamic(); + } + + // That's tricky - this filter will have no name. We need to generate one + // because our code relies on names. + AtomicInteger autoName = new AtomicInteger(); + + + @Override + public T createFilter(Class c) throws ServletException { + FilterConfigImpl fc = new FilterConfigImpl(this); + String filterName = "_tomcat_auto_filter_" + autoName.incrementAndGet(); + fc.setData(filterName, null, new HashMap()); + fc.setFilterClass(c); + filters.put(filterName, fc); + + try { + return (T) fc.createFilter(); + } catch (ClassCastException e) { + throw new ServletException(e); + } catch (ClassNotFoundException e) { + throw new ServletException(e); + } catch (IllegalAccessException e) { + throw new ServletException(e); + } catch (InstantiationException e) { + throw new ServletException(e); + } + } + + + @Override + public T createServlet(Class c) throws ServletException { + String filterName = "_tomcat_auto_servlet_" + autoName.incrementAndGet(); + ServletConfigImpl fc = new ServletConfigImpl(this, filterName, null); + fc.setServletClass(c); + servlets.put(filterName, fc); + + try { + return (T) fc.newInstance(); + } catch (ClassCastException e) { + throw new ServletException(e); + } + } + + + public FilterRegistration findFilterRegistration(String filterName) { + return filters.get(filterName); + } + + + public ServletRegistration findServletRegistration(String servletName) { + return servlets.get(servletName); + } + + + @Override + public boolean setInitParameter(String name, String value) { + HashMap params = contextConfig.contextParam; + return setInitParameter(this, params, name, value); + } + + + static Set setInitParameters(ServletContextImpl ctx, + Map params, + Map initParameters) + throws IllegalArgumentException, IllegalStateException { + if (ctx.startDone) { + throw new IllegalStateException(); + } + Set result = new HashSet(); + for (String name: initParameters.keySet()) { + String value = initParameters.get(name); + if (name == null || value == null) { + throw new IllegalArgumentException(); + } + if (!setInitParameter(ctx, params, name, value)) { + result.add(name); + } + } + return result; + } + + /** + * true if the context initialization parameter with the given name and value was set successfully on this ServletContext, and false if it was not set because this ServletContext already contains a context initialization parameter with a matching name + * Throws: + * java.lang.IllegalStateException - if this ServletContext has already been initialized + */ + static boolean setInitParameter(ServletContextImpl ctx, Map params, + String name, String value) { + if (name == null || value == null) { + throw new IllegalArgumentException(); + } + if (ctx.startDone) { + throw new IllegalStateException(); + } + String oldValue = params.get(name); + if (oldValue != null) { + return false; + } else { + params.put(name, value); + return true; + } + } + + public JspConfigDescriptor getJspConfigDescriptor() { + // fix me - just here to compile + return null; + } + + + + public void declareRoles(String... roleNames) { + // implement me + } + + public T createListener(Class c) throws ServletException { + // implement me + return null; + } + + public Collection getMappings() { + // implement me + return null; + } + + public Map getFilterRegistrations() { + // implement me + return null; + } + + public FilterRegistration getFilterRegistration(String filterName) { + // implement me + return null; + } + + public Map getServletRegistrations() { + // implement me + return null; + } + + public ServletRegistration getServletRegistration(String servletName) { + // implement me + return null; + } + + public int getEffectiveMinorVersion() { + // implement me + return -1; + } + + public int getEffectiveMajorVersion() { + // implement me + return -1; + } + + private final class LiteInstanceManager implements InstanceManager { + private ObjectManager om; + + public LiteInstanceManager(ObjectManager objectManager) { + this.om = objectManager; + } + + @Override + public void destroyInstance(Object o) + throws IllegalAccessException, + InvocationTargetException { + } + + @Override + public Object newInstance(String className) + throws IllegalAccessException, + InvocationTargetException, NamingException, + InstantiationException, + ClassNotFoundException { + return om.get(className); + } + + @Override + public Object newInstance(String fqcn, + ClassLoader classLoader) + throws IllegalAccessException, + InvocationTargetException, NamingException, + InstantiationException, + ClassNotFoundException { + return om.get(fqcn); + } + + @Override + public void newInstance(Object o) + throws IllegalAccessException, + InvocationTargetException, NamingException { + om.bind(o.getClass().getName(), o); + } + } + +} + diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletInputStreamImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletInputStreamImpl.java new file mode 100644 index 000000000..c566dc579 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletInputStreamImpl.java @@ -0,0 +1,107 @@ +/* + * 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.servlet; + +import java.io.IOException; +import java.io.InputStream; + +import javax.servlet.ServletInputStream; + + +/** + * Wrapper around MessageReader + * + * @author Remy Maucherat + * @author Jean-Francois Arcand + * @author Costin Manolache + */ +public class ServletInputStreamImpl extends ServletInputStream { + + + // ----------------------------------------------------- Instance Variables + + + protected InputStream ib; + + + // ----------------------------------------------------------- Constructors + + + public ServletInputStreamImpl(InputStream ib) { + this.ib = ib; + } + + // --------------------------------------------- ServletInputStream Methods + + public long skip(long n) + throws IOException { + return ib.skip(n); + } + + public void mark(int readAheadLimit) + { + //try { + ib.mark(readAheadLimit); +// } catch (IOException e) { +// e.printStackTrace(); +// } + } + + + public void reset() + throws IOException { + ib.reset(); + } + + + + public int read() + throws IOException { + return ib.read(); + } + + public int available() throws IOException { + return ib.available(); + } + + public int read(final byte[] b) throws IOException { + return ib.read(b, 0, b.length); + } + + + public int read(final byte[] b, final int off, final int len) + throws IOException { + + return ib.read(b, off, len); + } + + + public int readLine(byte[] b, int off, int len) throws IOException { + return super.readLine(b, off, len); + } + + /** + * Close the stream + * Since we re-cycle, we can't allow the call to super.close() + * which would permantely disable us. + */ + public void close() throws IOException { + ib.close(); + } + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletOutputStreamImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletOutputStreamImpl.java new file mode 100644 index 000000000..5f2225ae7 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletOutputStreamImpl.java @@ -0,0 +1,118 @@ +/* + * 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.servlet; + +import java.io.IOException; + +import javax.servlet.ServletOutputStream; + + +/** + * Coyote implementation of the servlet output stream. + * + * @author Costin Manolache + * @author Remy Maucherat + */ +public class ServletOutputStreamImpl + extends ServletOutputStream { + + + // ----------------------------------------------------- Instance Variables + + + protected BodyWriter ob; + + + // ----------------------------------------------------------- Constructors + + + public ServletOutputStreamImpl(BodyWriter ob) { + this.ob = ob; + } + + + // --------------------------------------------------------- Public Methods + + + /** + * Prevent cloning the facade. + */ + protected Object clone() + throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + + // -------------------------------------------------------- Package Methods + + + /** + * Clear facade. + */ + void clear() { + ob = null; + } + + + // --------------------------------------------------- OutputStream Methods + + + public void write(int i) + throws IOException { + ob.writeByte(i); + } + + + public void write(byte[] b) + throws IOException { + write(b, 0, b.length); + } + + + public void write(byte[] b, int off, int len) + throws IOException { + ob.write(b, off, len); + } + + + /** + * Will send the buffer to the client. + */ + public void flush() + throws IOException { + ob.flush(); + } + + + public void close() + throws IOException { + ob.close(); + } + + + // -------------------------------------------- ServletOutputStream Methods + + + public void print(String s) + throws IOException { + ob.write(s); + } + + +} + diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletReaderImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletReaderImpl.java new file mode 100644 index 000000000..62bfb2e6a --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletReaderImpl.java @@ -0,0 +1,181 @@ +/* + * 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.servlet; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + + + +/** + * Coyote implementation of the buffred reader. + * + * @author Remy Maucherat + */ +public class ServletReaderImpl + extends BufferedReader { + + + // -------------------------------------------------------------- Constants + + + private static final char[] LINE_SEP = { '\r', '\n' }; + private static final int MAX_LINE_LENGTH = 4096; + + + // ----------------------------------------------------- Instance Variables + + + protected Reader ib; + + + protected char[] lineBuffer = null; + + + // ----------------------------------------------------------- Constructors + + + public ServletReaderImpl(Reader ib) { + super(ib, 1); + this.ib = ib; + } + + public void close() + throws IOException { + ib.close(); + } + + + public int read() + throws IOException { + return ib.read(); + } + + + public int read(char[] cbuf) + throws IOException { + return ib.read(cbuf, 0, cbuf.length); + } + + + public int read(char[] cbuf, int off, int len) + throws IOException { + return ib.read(cbuf, off, len); + } + + + public long skip(long n) + throws IOException { + return ib.skip(n); + } + + + public boolean ready() + throws IOException { + return ib.ready(); + } + + + public boolean markSupported() { + return true; + } + + + public void mark(int readAheadLimit) + throws IOException { + ib.mark(readAheadLimit); + } + + + public void reset() + throws IOException { + ib.reset(); + } + + + // TODO: move the readLine functionality to base coyote IO + public String readLine() + throws IOException { + + if (lineBuffer == null) { + lineBuffer = new char[MAX_LINE_LENGTH]; + } + + String result = null; + + int pos = 0; + int end = -1; + int skip = -1; + StringBuilder aggregator = null; + while (end < 0) { + mark(MAX_LINE_LENGTH); + while ((pos < MAX_LINE_LENGTH) && (end < 0)) { + int nRead = read(lineBuffer, pos, MAX_LINE_LENGTH - pos); + if (nRead < 0) { + if (pos == 0) { + return null; + } + end = pos; + skip = pos; + } + for (int i = pos; (i < (pos + nRead)) && (end < 0); i++) { + if (lineBuffer[i] == LINE_SEP[0]) { + end = i; + skip = i + 1; + char nextchar; + if (i == (pos + nRead - 1)) { + nextchar = (char) read(); + } else { + nextchar = lineBuffer[i+1]; + } + if (nextchar == LINE_SEP[1]) { + skip++; + } + } else if (lineBuffer[i] == LINE_SEP[1]) { + end = i; + skip = i + 1; + } + } + if (nRead > 0) { + pos += nRead; + } + } + if (end < 0) { + if (aggregator == null) { + aggregator = new StringBuilder(); + } + aggregator.append(lineBuffer); + pos = 0; + } else { + reset(); + skip(skip); + } + } + + if (aggregator == null) { + result = new String(lineBuffer, 0, end); + } else { + aggregator.append(lineBuffer, 0, end); + result = aggregator.toString(); + } + + return result; + + } +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletRequestImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletRequestImpl.java new file mode 100644 index 000000000..ef22f6f7b --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletRequestImpl.java @@ -0,0 +1,2572 @@ +/* + * 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.servlet; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.logging.Level; + +import javax.security.auth.Subject; +import javax.servlet.AsyncContext; +import javax.servlet.AsyncListener; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletRequestAttributeEvent; +import javax.servlet.ServletRequestAttributeListener; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.Part; + +import org.apache.tomcat.servlets.session.UserSessionManager; +import org.apache.tomcat.servlets.util.Enumerator; +import org.apache.tomcat.servlets.util.LocaleParser; +import org.apache.tomcat.servlets.util.RequestUtil; +import org.apache.tomcat.util.buf.B2CConverter; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.buf.UriNormalizer; +import org.apache.tomcat.util.http.Cookies; +import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.HttpRequest; +import org.apache.tomcat.util.http.Parameters; +import org.apache.tomcat.util.http.ServerCookie; +import org.apache.tomcat.util.http.mapper.MappingData; + + +/** + * + * Wrapper object for the Coyote request. + * + * @author Remy Maucherat + * @author Craig R. McClanahan + */ +public class ServletRequestImpl implements HttpServletRequest { + + /** + * The request attribute under which we store the array of X509Certificate + * objects representing the certificate chain presented by our client, + * if any. + */ + public static final String CERTIFICATES_ATTR = + "javax.servlet.request.X509Certificate"; + + /** + * The request attribute under which we store the name of the cipher suite + * being used on an SSL connection (as an object of type + * java.lang.String). + */ + public static final String CIPHER_SUITE_ATTR = + "javax.servlet.request.cipher_suite"; + + /** + * Request dispatcher state. + */ + public static final String DISPATCHER_TYPE_ATTR = + "org.apache.catalina.core.DISPATCHER_TYPE"; + + /** + * Request dispatcher path. + */ + public static final String DISPATCHER_REQUEST_PATH_ATTR = + "org.apache.catalina.core.DISPATCHER_REQUEST_PATH"; + + /** + * The servlet context attribute under which we store the class path + * for our application class loader (as an object of type String), + * delimited with the appropriate path delimiter for this platform. + */ + public static final String CLASS_PATH_ATTR = + "org.apache.catalina.jsp_classpath"; + + + /** + * The request attribute under which we forward a Java exception + * (as an object of type Throwable) to an error page. + */ + public static final String EXCEPTION_ATTR = + "javax.servlet.error.exception"; + + + /** + * The request attribute under which we forward the request URI + * (as an object of type String) of the page on which an error occurred. + */ + public static final String EXCEPTION_PAGE_ATTR = + "javax.servlet.error.request_uri"; + + + /** + * The request attribute under which we forward a Java exception type + * (as an object of type Class) to an error page. + */ + public static final String EXCEPTION_TYPE_ATTR = + "javax.servlet.error.exception_type"; + + + /** + * The request attribute under which we forward an HTTP status message + * (as an object of type STring) to an error page. + */ + public static final String ERROR_MESSAGE_ATTR = + "javax.servlet.error.message"; + + + /** + * The request attribute under which we expose the value of the + * <jsp-file> value associated with this servlet, + * if any. + */ + public static final String JSP_FILE_ATTR = + "org.apache.catalina.jsp_file"; + + + /** + * The request attribute under which we store the key size being used for + * this SSL connection (as an object of type java.lang.Integer). + */ + public static final String KEY_SIZE_ATTR = + "javax.servlet.request.key_size"; + + /** + * The request attribute under which we store the session id being used + * for this SSL connection (as an object of type java.lang.String). + */ + public static final String SSL_SESSION_ID_ATTR = + "javax.servlet.request.ssl_session"; + + /** + * The request attribute under which we forward a servlet name to + * an error page. + */ + public static final String SERVLET_NAME_ATTR = + "javax.servlet.error.servlet_name"; + + + /** + * The name of the cookie used to pass the session identifier back + * and forth with the client. + */ + public static final String SESSION_COOKIE_NAME = "JSESSIONID"; + + + /** + * The name of the path parameter used to pass the session identifier + * back and forth with the client. + */ + public static final String SESSION_PARAMETER_NAME = "jsessionid"; + + + /** + * The request attribute under which we forward an HTTP status code + * (as an object of type Integer) to an error page. + */ + public static final String STATUS_CODE_ATTR = + "javax.servlet.error.status_code"; + + + /** + * The subject under which the AccessControlContext is running. + */ + public static final String SUBJECT_ATTR = + "javax.security.auth.subject"; + + + /** + * The servlet context attribute under which we store a temporary + * working directory (as an object of type File) for use by servlets + * within this web application. + */ + public static final String WORK_DIR_ATTR = + "javax.servlet.context.tempdir"; + + protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT"); + + + /** + * The default Locale if none are specified. + */ + protected static Locale defaultLocale = Locale.getDefault(); + + // ApplicationFilterFactory. What's the use ??? + private static Integer REQUEST_INTEGER = new Integer(8); + + /** + * The match string for identifying a session ID parameter. + */ + private static final String match = ";" + SESSION_PARAMETER_NAME + "="; + + /** + * The set of cookies associated with this Request. + */ + protected Cookie[] cookies = null; + + + /** + * The set of SimpleDateFormat formats to use in getDateHeader(). + * + * Notice that because SimpleDateFormat is not thread-safe, we can't + * declare formats[] as a static variable. + */ + protected SimpleDateFormat formats[] = null; + + + /** + * The attributes associated with this Request, keyed by attribute name. + */ + protected HashMap attributes = new HashMap(); + + + /** + * List of read only attributes for this Request. + */ + //private HashMap readOnlyAttributes = new HashMap(); + + + /** + * The preferred Locales assocaited with this Request. + */ + protected ArrayList locales = new ArrayList(); + + + /** + * Authentication type. + */ + protected String authType = null; + + /** + * User principal. + */ + protected Principal userPrincipal = null; + + + /** + * The Subject associated with the current AccessControllerContext + */ + protected transient Subject subject = null; + + + /** + * The current dispatcher type. + */ + protected Object dispatcherType = null; + + + /** + * The associated input buffer. + */ + protected BodyReader inputBuffer; + + Connector connector; + + /** + * ServletInputStream. + */ + protected ServletInputStreamImpl inputStream; + + + /** + * Reader. + */ + protected BufferedReader reader; + + + /** + * Using stream flag. + */ + protected boolean usingInputStream = false; + + + /** + * Using writer flag. + */ + protected boolean usingReader = false; + + + /** + * Session parsed flag. + */ + protected boolean sessionParsed = false; + + + /** + * Request parameters parsed flag. + */ + protected boolean parametersParsed = false; + + + /** + * Cookies parsed flag. + */ + protected boolean cookiesParsed = false; + + + /** + * Secure flag. + */ + protected boolean secure = false; + + + /** + * Hash map used in the getParametersMap method. + */ + protected ParameterMap parameterMap = new ParameterMap(); + + + /** + * The currently active session for this request. + */ + protected HttpSession session = null; + + + /** + * The current request dispatcher path. + */ + protected Object requestDispatcherPath = null; + + + /** + * Was the requested session ID received in a cookie? + */ + protected boolean requestedSessionCookie = false; + + + /** + * The requested session ID (if any) for this request. + */ + protected String requestedSessionId = null; + + + /** + * Was the requested session ID received in a URL? + */ + protected boolean requestedSessionURL = false; + + + /** + * Parse locales. + */ + protected boolean localesParsed = false; + + + /** + * Associated context. + */ + protected ServletContextImpl context = null; + + + + // --------------------------------------------------------- Public Methods + + /** + * Filter chain associated with the request. + */ + protected FilterChainImpl filterChain = new FilterChainImpl(); + + /** + * Mapping data. + */ + protected MappingData mappingData = new MappingData(); + + + // -------------------------------------------------------- Request Methods + + /** + * The response with which this request is associated. + */ + protected ServletResponseImpl response = new ServletResponseImpl(); + + /** + * URI byte to char converter (not recycled). + */ + // protected B2CConverter URIConverter = null; + + /** + * Associated wrapper. + */ + protected ServletConfigImpl wrapper = null; + + /** + * Post data buffer. + */ + public final static int CACHED_POST_LEN = 8192; + + public byte[] postData = null; + + + private HttpRequest httpRequest; + + /** New IO/buffer model + */ + //protected Http11Connection con; + + + public ServletRequestImpl() { + response.setRequest(this); + } + + +// /** +// * Return the Host within which this Request is being processed. +// */ +// public Host getHost() { +// if (getContext() == null) +// return null; +// return (Host) getContext().getParent(); +// //return ((Host) mappingData.host); +// } +// +// +// /** +// * Set the Host within which this Request is being processed. This +// * must be called as soon as the appropriate Host is identified, and +// * before the Request is passed to a context. +// * +// * @param host The newly associated Host +// */ +// public void setHost(Host host) { +// mappingData.host = host; +// } + + /** + * Add a Cookie to the set of Cookies associated with this Request. + * + * @param cookie The new cookie + */ + public void addCookie(Cookie cookie) { + + if (!cookiesParsed) + parseCookies(); + + int size = 0; + if (cookies != null) { + size = cookies.length; + } + + Cookie[] newCookies = new Cookie[size + 1]; + for (int i = 0; i < size; i++) { + newCookies[i] = cookies[i]; + } + newCookies[size] = cookie; + + cookies = newCookies; + + } + + /** + * Add a Header to the set of Headers associated with this Request. + * + * @param name The new header name + * @param value The new header value + */ + public void addHeader(String name, String value) { + // Not used + } + + public void setConnector(Connector c) { + connector = c; + } + + public Connector getConnector() { + return connector; + } + + /** + * Add a Locale to the set of preferred Locales for this Request. The + * first added Locale will be the first one returned by getLocales(). + * + * @param locale The new preferred Locale + */ + public void addLocale(Locale locale) { + locales.add(locale); + } + + + /** + * Add a parameter name and corresponding set of values to this Request. + * (This is used when restoring the original request on a form based + * login). + * + * @param name Name of this request parameter + * @param values Corresponding values for this request parameter + */ + public void addParameter(String name, String values[]) { + httpRequest.getParameters().addParameterValues(name, values); + } + + /** + * Clear the collection of Cookies associated with this Request. + */ + public void clearCookies() { + cookiesParsed = true; + cookies = null; + } + + /** + * Clear the collection of Headers associated with this Request. + */ + public void clearHeaders() { + // Not used + } + + /** + * Clear the collection of Locales associated with this Request. + */ + public void clearLocales() { + locales.clear(); + } + + /** + * Clear the collection of parameters associated with this Request. + */ + public void clearParameters() { + // Not used + } + + + /** + * Create and return a ServletInputStream to read the content + * associated with this Request. + * + * @exception IOException if an input/output error occurs + */ + public ServletInputStream createInputStream() + throws IOException { + return inputStream; + } + + /** + * Perform whatever actions are required to flush and close the input + * stream or reader, in a single operation. + * + * @exception IOException if an input/output error occurs + */ + public void finishRequest() throws IOException { + // The reader and input stream don't need to be closed + } + + + /** + * Return the specified request attribute if it exists; otherwise, return + * null. + * + * @param name Name of the request attribute to return + */ + public Object getAttribute(String name) { + + if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { + return (dispatcherType == null) + ? REQUEST_INTEGER + : dispatcherType; + } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { + return (requestDispatcherPath == null) + ? getRequestPathMB().toString() + : requestDispatcherPath.toString(); + } + + Object attr=attributes.get(name); + + if(attr!=null) + return(attr); + +// attr = reqB.getAttribute(name); +// if(attr != null) +// return attr; +// if( isSSLAttribute(name) ) { +// reqB.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE, +// reqB); +// attr = reqB.getAttribute(ServletRequestImpl.CERTIFICATES_ATTR); +// if( attr != null) { +// attributes.put(ServletRequestImpl.CERTIFICATES_ATTR, attr); +// } +// attr = reqB.getAttribute(ServletRequestImpl.CIPHER_SUITE_ATTR); +// if(attr != null) { +// attributes.put(ServletRequestImpl.CIPHER_SUITE_ATTR, attr); +// } +// attr = reqB.getAttribute(ServletRequestImpl.KEY_SIZE_ATTR); +// if(attr != null) { +// attributes.put(ServletRequestImpl.KEY_SIZE_ATTR, attr); +// } +// attr = reqB.getAttribute(ServletRequestImpl.SSL_SESSION_ID_ATTR); +// if(attr != null) { +// attributes.put(ServletRequestImpl.SSL_SESSION_ID_ATTR, attr); +// } +// attr = attributes.get(name); +// } + return attr; + } + + /** + * Return the names of all request attributes for this Request, or an + * empty Enumeration if there are none. + */ + public Enumeration getAttributeNames() { + if (isSecure()) { + getAttribute(ServletRequestImpl.CERTIFICATES_ATTR); + } + return new Enumerator(attributes.keySet(), true); + } + + + /** + * Return the authentication type used for this Request. + */ + public String getAuthType() { + return (authType); + } + + + // ------------------------------------------------- Request Public Methods + + + /** + * Return the character encoding for this Request. + */ + public String getCharacterEncoding() { + return (httpRequest.getCharacterEncoding()); + } + + + /** + * Return the content length for this Request. + */ + public int getContentLength() { + return (httpRequest.getContentLength()); + } + + +// /** +// * Return the object bound with the specified name to the internal notes +// * for this request, or null if no such binding exists. +// * +// * @param name Name of the note to be returned +// */ +// public Object getNote(String name) { +// return (notes.get(name)); +// } +// +// +// /** +// * Return an Iterator containing the String names of all notes bindings +// * that exist for this request. +// */ +// public Iterator getNoteNames() { +// return (notes.keySet().iterator()); +// } +// +// +// /** +// * Remove any object bound to the specified name in the internal notes +// * for this request. +// * +// * @param name Name of the note to be removed +// */ +// public void removeNote(String name) { +// notes.remove(name); +// } +// +// +// /** +// * Bind an object to a specified name in the internal notes associated +// * with this request, replacing any existing binding for this name. +// * +// * @param name Name to which the object should be bound +// * @param value Object to be bound to the specified name +// */ +// public void setNote(String name, Object value) { +// notes.put(name, value); +// } +// + + /** + * Return the content type for this Request. + */ + public String getContentType() { + return (httpRequest.getContentType()); + } + + + /** + * Return the Context within which this Request is being processed. + */ + public ServletContextImpl getContext() { + return (this.context); + } + + + /** + * Return the portion of the request URI used to select the Context + * of the Request. + */ + public String getContextPath() { + return (mappingData.contextPath.toString()); + } + + + /** + * Get the context path. + * + * @return the context path + */ + public MessageBytes getContextPathMB() { + return (mappingData.contextPath); + } + + + /** + * Return the set of Cookies received with this Request. + */ + public Cookie[] getCookies() { + + if (!cookiesParsed) + parseCookies(); + + return cookies; + + } + + + /** + * Return the value of the specified date header, if any; otherwise + * return -1. + * + * @param name Name of the requested date header + * + * @exception IllegalArgumentException if the specified header value + * cannot be converted to a date + */ + public long getDateHeader(String name) { + + String value = getHeader(name); + if (value == null) + return (-1L); + if (formats == null) { + formats = new SimpleDateFormat[] { + new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), + new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), + new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) + }; + formats[0].setTimeZone(GMT_ZONE); + formats[1].setTimeZone(GMT_ZONE); + formats[2].setTimeZone(GMT_ZONE); + } + + // Attempt to convert the date header in a variety of formats + long result = FastHttpDateFormat.parseDate(value, formats); + if (result != (-1L)) { + return result; + } + throw new IllegalArgumentException(value); + + } + + + /** + * Get the decoded request URI. + * + * @return the URL decoded request URI + */ + public String getDecodedRequestURI() { + return (httpRequest.decodedURI().toString()); + } + + + /** + * Get the decoded request URI. + * + * @return the URL decoded request URI + */ + public MessageBytes getDecodedRequestURIMB() { + return (httpRequest.decodedURI()); + } + + + /** + * Get filter chain associated with the request. + */ + public FilterChainImpl getFilterChain() { + return (this.filterChain); + } + + + // ------------------------------------------------- ServletRequest Methods + + /** + * Return the first value of the specified header, if any; otherwise, + * return null + * + * @param name Name of the requested header + */ + public String getHeader(String name) { + return httpRequest.getHeader(name); + } + + /** + * Return the names of all headers received with this request. + */ + public Enumeration getHeaderNames() { + return httpRequest.getMimeHeaders().names(); + } + + + /** + * Return all of the values of the specified header, if any; otherwise, + * return an empty enumeration. + * + * @param name Name of the requested header + */ + public Enumeration getHeaders(String name) { + return httpRequest.getMimeHeaders().values(name); + } + + /** + * Return the servlet input stream for this Request. The default + * implementation returns a servlet input stream created by + * createInputStream(). + * + * @exception IllegalStateException if getReader() has + * already been called for this request + * @exception IOException if an input/output error occurs + */ + public ServletInputStream getInputStream() throws IOException { + + if (usingReader) + throw new IllegalStateException + ("usingReader"); + + usingInputStream = true; + return inputStream; + + } + + + /** + * Return the value of the specified header as an integer, or -1 if there + * is no such header for this request. + * + * @param name Name of the requested header + * + * @exception IllegalArgumentException if the specified header value + * cannot be converted to an integer + */ + public int getIntHeader(String name) { + + String value = getHeader(name); + if (value == null) { + return (-1); + } else { + return (Integer.parseInt(value)); + } + + } + + + /** + * Returns the Internet Protocol (IP) address of the interface on + * which the request was received. + */ + public String getLocalAddr(){ + return httpRequest.localAddr().toString(); + } + + + /** + * Return the preferred Locale that the client will accept content in, + * based on the value for the first Accept-Language header + * that was encountered. If the request did not specify a preferred + * language, the server's default Locale is returned. + */ + public Locale getLocale() { + + if (!localesParsed) + parseLocales(); + + if (locales.size() > 0) { + return ((Locale) locales.get(0)); + } else { + return (defaultLocale); + } + + } + + + /** + * Return the set of preferred Locales that the client will accept + * content in, based on the values for any Accept-Language + * headers that were encountered. If the request did not specify a + * preferred language, the server's default Locale is returned. + */ + public Enumeration getLocales() { + + if (!localesParsed) + parseLocales(); + + if (locales.size() > 0) + return (new Enumerator(locales)); + ArrayList results = new ArrayList(); + results.add(defaultLocale); + return (new Enumerator(results)); + + } + + + /** + * Returns the host name of the Internet Protocol (IP) interface on + * which the request was received. + */ + public String getLocalName(){ + return httpRequest.localName().toString(); + } + + + /** + * Returns the Internet Protocol (IP) port number of the interface + * on which the request was received. + */ + public int getLocalPort(){ + return httpRequest.getLocalPort(); + } + + + /** + * Return mapping data. + */ + public MappingData getMappingData() { + return (mappingData); + } + + + + /** + * Return the HTTP request method used in this Request. + */ + public String getMethod() { + return httpRequest.method().toString(); + } + + + /** + * Return the value of the specified request parameter, if any; otherwise, + * return null. If there is more than one value defined, + * return only the first one. + * + * @param name Name of the desired request parameter + */ + public String getParameter(String name) { + + if (!parametersParsed) + parseParameters(); + + return httpRequest.getParameters().getParameter(name); + + } + + + /** + * Returns a Map of the parameters of this request. + * Request parameters are extra information sent with the request. + * For HTTP servlets, parameters are contained in the query string + * or posted form data. + * + * @return A Map containing parameter names as keys + * and parameter values as map values. + */ + public Map getParameterMap() { + + if (parameterMap.isLocked()) + return parameterMap; + + Enumeration enumeration = getParameterNames(); + while (enumeration.hasMoreElements()) { + String name = enumeration.nextElement().toString(); + String[] values = getParameterValues(name); + parameterMap.put(name, values); + } + + parameterMap.setLocked(true); + + return parameterMap; + + } + + + /** + * Return the names of all defined request parameters for this request. + */ + public Enumeration getParameterNames() { + + if (!parametersParsed) + parseParameters(); + + return httpRequest.getParameters().getParameterNames(); + + } + + + /** + * Return the defined values for the specified request parameter, if any; + * otherwise, return null. + * + * @param name Name of the desired request parameter + */ + public String[] getParameterValues(String name) { + + if (!parametersParsed) + parseParameters(); + + return httpRequest.getParameters().getParameterValues(name); + + } + + + /** + * Return the path information associated with this Request. + */ + public String getPathInfo() { + return (mappingData.pathInfo.toString()); + } + + + /** + * Get the path info. + * + * @return the path info + */ + public MessageBytes getPathInfoMB() { + return (mappingData.pathInfo); + } + + + /** + * Return the extra path information for this request, translated + * to a real path. + */ + public String getPathTranslated() { + + if (context == null) + return (null); + + if (getPathInfo() == null) { + return (null); + } else { + return (context.getServletContext().getRealPath(getPathInfo())); + } + + } + + /** + * Return the principal that has been authenticated for this Request. + */ + public Principal getPrincipal() { + return (userPrincipal); + } + + /** + * Return the protocol and version used to make this Request. + */ + public String getProtocol() { + return httpRequest.protocol().toString(); + } + + /** + * Return the query string associated with this request. + */ + public String getQueryString() { + String queryString = httpRequest.queryString().toString(); + if (queryString == null || queryString.equals("")) { + return (null); + } else { + return queryString; + } + } + + + /** + * Read the Reader wrapping the input stream for this Request. The + * default implementation wraps a BufferedReader around the + * servlet input stream returned by createInputStream(). + * + * @exception IllegalStateException if getInputStream() + * has already been called for this request + * @exception IOException if an input/output error occurs + */ + public BufferedReader getReader() throws IOException { + + if (usingInputStream) + throw new IllegalStateException + ("usingInputStream"); + + usingReader = true; + //inputBuffer.setConverter();// getB2C()); + return reader; + + } + + /** + * Cached list of encoders. + */ + protected HashMap encoders = new HashMap(); + + public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1"; + + /** + * Converter for the encoding associated with the request. + * If encoding is changed - a different encoder will be returned. + * + * Encoders are cached ( per request ) - at least 8K per charset + */ + public B2CConverter getB2C() throws IOException { + String enc = getCharacterEncoding(); + if (enc == null) { + enc = DEFAULT_CHARACTER_ENCODING; + } + B2CConverter conv = + (B2CConverter) encoders.get(enc); + if (conv == null) { + conv = new B2CConverter(enc); + encoders.put(enc, conv); + } + return conv; + } + + /** + * Return the real path of the specified virtual path. + * + * @param path Path to be translated + * + * @deprecated As of version 2.1 of the Java Servlet API, use + * ServletContext.getRealPath(). + */ + public String getRealPath(String path) { + + if (context == null) + return (null); + ServletContext servletContext = context; // .getServletContext(); + if (servletContext == null) + return (null); + else { + try { + return (servletContext.getRealPath(path)); + } catch (IllegalArgumentException e) { + return (null); + } + } + + } + + + /** + * Return the remote IP address making this Request. + */ + public String getRemoteAddr() { + if (httpRequest.remoteAddr().isNull()) { + httpRequest.remoteAddr().setString(connector.getRemoteAddr(this)); + } + return httpRequest.remoteAddr().toString(); + } + + + /** + * Return the remote host name making this Request. + */ + public String getRemoteHost() { + if (httpRequest.remoteHost().isNull()) { + httpRequest.remoteHost().setString(connector.getRemoteHost(this)); + } + return httpRequest.remoteHost().toString(); + } + + + /** + * Returns the Internet Protocol (IP) source port of the client + * or last proxy that sent the request. + */ + public int getRemotePort(){ + return httpRequest.getRemotePort(); + } + + + /** + * Return the name of the remote user that has been authenticated + * for this Request. + */ + public String getRemoteUser() { + + if (userPrincipal != null) { + return (userPrincipal.getName()); + } else { + return (null); + } + + } + + + /** + * Return the ServletRequest for which this object + * is the facade. This method must be implemented by a subclass. + */ + public HttpServletRequest getRequest() { + return this; + } + + public HttpRequest getHttpRequest() { + return httpRequest; + } + + public void setHttpRequest(HttpRequest req, BodyReader in) { + this.httpRequest = req; + inputBuffer = in; + inputStream = new ServletInputStreamImpl(inputBuffer.asInputStream()); + reader = new ServletReaderImpl(inputBuffer); + } + + public BodyReader getBodyReader() { + return inputBuffer; + } + + /** + * Return a RequestDispatcher that wraps the resource at the specified + * path, which may be interpreted as relative to the current request path. + * + * @param path Path of the resource to be wrapped + */ + public RequestDispatcher getRequestDispatcher(String path) { + + if (context == null) + return (null); + + // If the path is already context-relative, just pass it through + if (path == null) + return (null); + else if (path.startsWith("/")) + return (context.getRequestDispatcher(path)); + + // Convert a request-relative path to a context-relative one + String servletPath = (String) getAttribute(RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR); + if (servletPath == null) + servletPath = getServletPath(); + + // Add the path info, if there is any + String pathInfo = getPathInfo(); + String requestPath = null; + + if (pathInfo == null) { + requestPath = servletPath; + } else { + requestPath = servletPath + pathInfo; + } + + int pos = requestPath.lastIndexOf('/'); + String relative = null; + if (pos >= 0) { + relative = RequestUtil.normalize + (requestPath.substring(0, pos + 1) + path); + } else { + relative = RequestUtil.normalize(requestPath + path); + } + + return (context.getRequestDispatcher(relative)); + + } + + + /** + * Return the session identifier included in this request, if any. + */ + public String getRequestedSessionId() { + return (requestedSessionId); + } + + + // ---------------------------------------------------- HttpRequest Methods + + + /** + * Get the request path. + * + * @return the request path + */ + public MessageBytes getRequestPathMB() { + return (mappingData.requestPath); + } + + + /** + * Return the request URI for this request. + */ + public String getRequestURI() { + return httpRequest.requestURI().toString(); + } + + /** + */ + public void setRequestURI(String uri) { + httpRequest.decodedURI().setString(uri); + try { + UriNormalizer.decodeRequest(httpRequest.decodedURI(), + httpRequest.requestURI(), httpRequest.getURLDecoder()); + } catch(IOException ioe) { + ioe.printStackTrace(); + return; + } + } + + + + /** + * Reconstructs the URL the client used to make the request. + * The returned URL contains a protocol, server name, port + * number, and server path, but it does not include query + * string parameters. + *

+ * Because this method returns a StringBuffer, + * not a String, you can modify the URL easily, + * for example, to append query parameters. + *

+ * This method is useful for creating redirect messages and + * for reporting errors. + * + * @return A StringBuffer object containing the + * reconstructed URL + */ + public StringBuffer getRequestURL() { + + StringBuffer url = new StringBuffer(); + String scheme = getScheme(); + int port = getServerPort(); + if (port < 0) + port = 80; // Work around java.net.URL bug + + url.append(scheme); + url.append("://"); + url.append(getServerName()); + if ((scheme.equals("http") && (port != 80)) + || (scheme.equals("https") && (port != 443))) { + url.append(':'); + url.append(port); + } + url.append(getRequestURI()); + + return (url); + + } + + + /** + * Return the Response with which this Request is associated. + */ + public ServletResponseImpl getResponse() { + return (this.response); + } + + + /** + * Return the scheme used to make this Request. + */ + public String getScheme() { + String scheme = httpRequest.scheme().toString(); + if (scheme == null) { + scheme = (isSecure() ? "https" : "http"); + } + return scheme; + } + + + /** + * Return the server name responding to this Request. + */ + public String getServerName() { + return (httpRequest.serverName().toString()); + } + + + /** + * Return the server port responding to this Request. + */ + public int getServerPort() { + return (httpRequest.getServerPort()); + } + + + /** + * Return the portion of the request URI used to select the servlet + * that will process this request. + */ + public String getServletPath() { + return (mappingData.wrapperPath.toString()); + } + + + /** + * Get the servlet path. + * + * @return the servlet path + */ + public MessageBytes getServletPathMB() { + return (mappingData.wrapperPath); + } + + + + /** + * Return the input stream associated with this Request. + */ + public InputStream getStream() { + return inputStream; + } + + + /** + * Return the principal that has been authenticated for this Request. + */ + public Principal getUserPrincipal() { + return userPrincipal; + } + + + /** + * Return the Wrapper within which this Request is being processed. + */ + public ServletConfigImpl getWrapper() { + return (this.wrapper); + } + + + /** + * Return true if the session identifier included in this + * request came from a cookie. + */ + public boolean isRequestedSessionIdFromCookie() { + + if (requestedSessionId != null) + return (requestedSessionCookie); + else + return (false); + + } + + + /** + * Return true if the session identifier included in this + * request came from the request URI. + * + * @deprecated As of Version 2.1 of the Java Servlet API, use + * isRequestedSessionIdFromURL() instead. + */ + public boolean isRequestedSessionIdFromUrl() { + return (isRequestedSessionIdFromURL()); + } + + + /** + * Return true if the session identifier included in this + * request came from the request URI. + */ + public boolean isRequestedSessionIdFromURL() { + + if (requestedSessionId != null) + return (requestedSessionURL); + else + return (false); + + } + + + /** + * Return true if the session identifier included in this + * request identifies a valid session. + */ + public boolean isRequestedSessionIdValid() { + + if (requestedSessionId == null) + return (false); + if (context == null) + return (false); + UserSessionManager manager = context.getManager(); + if (manager == null) + return (false); + HttpSession session = null; + try { + session = manager.findSession(requestedSessionId); + } catch (IOException e) { + session = null; + } + if ((session != null) && manager.isValid(session)) + return (true); + else + return (false); + + } + + /** + * Was this request received on a secure connection? + */ + public boolean isSecure() { + return (secure); + } + + + /** + * Return true if the authenticated user principal + * possesses the specified role name. + * + * @param role Role name to be validated + */ + public boolean isUserInRole(String role) { + // Have we got an authenticated principal at all? + Principal userPrincipal = getPrincipal(); + if (userPrincipal == null) + return (false); + + // Identify the Realm we will use for checking role assignmenets + if (context == null) + return (false); + + // Check for a role alias defined in a element + if (wrapper != null) { + String realRole = wrapper.getSecurityRoleRef(role); + if (realRole != null) { + role = realRole; + } + } + + if (role.equals(userPrincipal.getName())) { + return true; + } + + // TODO: check !!!! + // Check for a role defined directly as a + return false; + } + + /** + * Release all object references, and initialize instance variables, in + * preparation for reuse of this object. + */ + public void recycle() { + + wrapper = null; + + dispatcherType = null; + requestDispatcherPath = null; + + authType = null; + inputBuffer.recycle(); + usingInputStream = false; + usingReader = false; + userPrincipal = null; + subject = null; + sessionParsed = false; + parametersParsed = false; + cookiesParsed = false; + locales.clear(); + localesParsed = false; + secure = false; + + attributes.clear(); + //notes.clear(); + cookies = null; + + if (session != null) { + context.getManager().endAccess(session); + } + context = null; + session = null; + requestedSessionCookie = false; + requestedSessionId = null; + requestedSessionURL = false; + + parameterMap.setLocked(false); + parameterMap.clear(); + + mappingData.recycle(); + httpRequest.recycle(); + } + + + /** + * Remove the specified request attribute if it exists. + * + * @param name Name of the request attribute to remove + */ + public void removeAttribute(String name) { + Object value = null; + boolean found = false; + + // Remove the specified attribute + // Check for read only attribute + // requests are per thread so synchronization unnecessary +// if (readOnlyAttributes.containsKey(name)) { +// return; +// } + found = attributes.containsKey(name); + if (found) { + value = attributes.get(name); + attributes.remove(name); + } else { + return; + } + + // Notify interested application event listeners + List listeners = context.getListeners(); + if (listeners.size() == 0) + return; + ServletRequestAttributeEvent event = null; + for (int i = 0; i < listeners.size(); i++) { + if (!(listeners.get(i) instanceof ServletRequestAttributeListener)) + continue; + ServletRequestAttributeListener listener = + (ServletRequestAttributeListener) listeners.get(i); + try { + if (event == null) { + event = + new ServletRequestAttributeEvent(context.getServletContext(), + getRequest(), name, value); + } + listener.attributeRemoved(event); + } catch (Throwable t) { + context.getLogger().log(Level.WARNING, "ServletRequestAttributeListner.attributeRemoved()", t); + // Error valve will pick this execption up and display it to user + attributes.put( ServletRequestImpl.EXCEPTION_ATTR, t ); + } + } + } + + + /** + * Set the specified request attribute to the specified value. + * + * @param name Name of the request attribute to set + * @param value The associated value + */ + public void setAttribute(String name, Object value) { + + // Name cannot be null + if (name == null) + throw new IllegalArgumentException + ("setAttribute() name == null"); + + // Null value is the same as removeAttribute() + if (value == null) { + removeAttribute(name); + return; + } + + if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { + dispatcherType = value; + return; + } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { + requestDispatcherPath = value; + return; + } + + Object oldValue = null; + boolean replaced = false; + + // Add or replace the specified attribute + // Check for read only attribute + // requests are per thread so synchronization unnecessary +// if (readOnlyAttributes.containsKey(name)) { +// return; +// } + + oldValue = attributes.put(name, value); + if (oldValue != null) { + replaced = true; + } + + // Pass special attributes to the native layer +// if (name.startsWith("org.apache.tomcat.")) { +// reqB.setAttribute(name, value); +// } +// + // Notify interested application event listeners + List listeners = context.getListeners(); + if (listeners.size() == 0) + return; + ServletRequestAttributeEvent event = null; + + for (int i = 0; i < listeners.size(); i++) { + if (!(listeners.get(i) instanceof ServletRequestAttributeListener)) + continue; + ServletRequestAttributeListener listener = + (ServletRequestAttributeListener) listeners.get(i); + try { + if (event == null) { + if (replaced) + event = + new ServletRequestAttributeEvent(context.getServletContext(), + getRequest(), name, oldValue); + else + event = + new ServletRequestAttributeEvent(context.getServletContext(), + getRequest(), name, value); + } + if (replaced) { + listener.attributeReplaced(event); + } else { + listener.attributeAdded(event); + } + } catch (Throwable t) { + context.getLogger().log(Level.WARNING, "ServletRequestAttributeListener error", t); + // Error valve will pick this execption up and display it to user + attributes.put( ServletRequestImpl.EXCEPTION_ATTR, t ); + } + } + } + + + // --------------------------------------------- HttpServletRequest Methods + + + /** + * Set the authentication type used for this request, if any; otherwise + * set the type to null. Typical values are "BASIC", + * "DIGEST", or "SSL". + * + * @param type The authentication type used + */ + public void setAuthType(String type) { + this.authType = type; + } + + + /** + * Overrides the name of the character encoding used in the body of + * this request. This method must be called prior to reading request + * parameters or reading input using getReader(). + * + * @param enc The character encoding to be used + * + * @exception UnsupportedEncodingException if the specified encoding + * is not supported + * + * @since Servlet 2.3 + */ + public void setCharacterEncoding(String enc) + throws UnsupportedEncodingException { + + // Ensure that the specified encoding is valid + byte buffer[] = new byte[1]; + buffer[0] = (byte) 'a'; + String dummy = new String(buffer, enc); + + // Save the validated encoding + httpRequest.setCharacterEncoding(enc); + + } + + +// public void setConnection(Http11Connection con) { +// this.con = con; +// //reqB.messageWriter.setConnection(con); +// //inputBuffer.setRequest(req); +// } + + + /** + * Set the content length associated with this Request. + * + * @param length The new content length + */ + public void setContentLength(int length) { + // Not used + } + + + /** + * Set the content type (and optionally the character encoding) + * associated with this Request. For example, + * text/html; charset=ISO-8859-4. + * + * @param type The new content type + */ + public void setContentType(String type) { + // Not used + } + + + /** + * Set the Context within which this Request is being processed. This + * must be called as soon as the appropriate Context is identified, because + * it identifies the value to be returned by getContextPath(), + * and thus enables parsing of the request URI. + * + * @param context The newly associated Context + */ + public void setContext(ServletContextImpl context) { + this.context = context; + } + + + /** + * Set the context path for this Request. This will normally be called + * when the associated Context is mapping the Request to a particular + * Wrapper. + * + * @param path The context path + */ + public void setContextPath(String path) { + + if (path == null) { + mappingData.contextPath.setString(""); + } else { + mappingData.contextPath.setString(path); + } + + } + + + /** + * Set the set of cookies recieved with this Request. + */ + public void setCookies(Cookie[] cookies) { + + this.cookies = cookies; + + } + + + /** + * Set the decoded request URI. + * + * @param uri The decoded request URI + */ + public void setDecodedRequestURI(String uri) { + // Not used + } + + + /** + * Set the HTTP request method used for this Request. + * + * @param method The request method + */ + public void setMethod(String method) { + httpRequest.method().setString(method); + } + + + /** + * Set the path information for this Request. This will normally be called + * when the associated Context is mapping the Request to a particular + * Wrapper. + * + * @param path The path information + */ + public void setPathInfo(String path) { + mappingData.pathInfo.setString(path); + } + + + /** + * Set the protocol name and version associated with this Request. + * + * @param protocol Protocol name and version + */ + public void setProtocol(String protocol) { + // Not used + } + + + /** + * Set the query string for this Request. This will normally be called + * by the HTTP Connector, when it parses the request headers. + * + * @param query The query string + */ + public void setQueryString(String query) { + // Not used + } + + + /** + * Set the IP address of the remote client associated with this Request. + * + * @param remoteAddr The remote IP address + */ + public void setRemoteAddr(String remoteAddr) { + // Not used + } + + + /** + * Set the fully qualified name of the remote client associated with this + * Request. + * + * @param remoteHost The remote host name + */ + public void setRemoteHost(String remoteHost) { + // Not used + } + + + /** + * Set a flag indicating whether or not the requested session ID for this + * request came in through a cookie. This is normally called by the + * HTTP Connector, when it parses the request headers. + * + * @param flag The new flag + */ + public void setRequestedSessionCookie(boolean flag) { + + this.requestedSessionCookie = flag; + + } + + + /** + * Set the requested session ID for this request. This is normally called + * by the HTTP Connector, when it parses the request headers. + * + * @param id The new session id + */ + public void setRequestedSessionId(String id) { + + this.requestedSessionId = id; + + } + + + /** + * Set a flag indicating whether or not the requested session ID for this + * request came in through a URL. This is normally called by the + * HTTP Connector, when it parses the request headers. + * + * @param flag The new flag + */ + public void setRequestedSessionURL(boolean flag) { + + this.requestedSessionURL = flag; + + } + + + /** + * Set the name of the scheme associated with this request. Typical values + * are http, https, and ftp. + * + * @param scheme The scheme + */ + public void setScheme(String scheme) { + // Not used + } + + + /** + * Set the value to be returned by isSecure() + * for this Request. + * + * @param secure The new isSecure value + */ + public void setSecure(boolean secure) { + this.secure = secure; + } + + + /** + * Set the name of the server (virtual host) to process this request. + * + * @param name The server name + */ + public void setServerName(String name) { + httpRequest.serverName().setString(name); + } + + + /** + * Set the port number of the server to process this request. + * + * @param port The server port + */ + public void setServerPort(int port) { + httpRequest.setServerPort(port); + } + + + /** + * Set the servlet path for this Request. This will normally be called + * when the associated Context is mapping the Request to a particular + * Wrapper. + * + * @param path The servlet path + */ + public void setServletPath(String path) { + if (path != null) + mappingData.wrapperPath.setString(path); + } + + + /** + * Set the input stream associated with this Request. + * + * @param stream The new input stream + */ + public void setStream(InputStream stream) { + // Ignore + } + + + /** + * Set the Principal who has been authenticated for this Request. This + * value is also used to calculate the value to be returned by the + * getRemoteUser() method. + * + * @param principal The user Principal + */ + public void setUserPrincipal(Principal principal) { + + if (System.getSecurityManager() != null){ + HttpSession session = getSession(false); + if ( (subject != null) && + (!subject.getPrincipals().contains(principal)) ){ + subject.getPrincipals().add(principal); + } else if (session != null && + session.getAttribute(ServletRequestImpl.SUBJECT_ATTR) == null) { + subject = new Subject(); + subject.getPrincipals().add(principal); + } + if (session != null){ + session.setAttribute(ServletRequestImpl.SUBJECT_ATTR, subject); + } + } + + this.userPrincipal = principal; + } + + + /** + * Set the Wrapper within which this Request is being processed. This + * must be called as soon as the appropriate Wrapper is identified, and + * before the Request is ultimately passed to an application servlet. + * @param wrapper The newly associated Wrapper + */ + public void setWrapper(ServletConfigImpl wrapper) { + this.wrapper = wrapper; + } + + + public String toString() { + return httpRequest.requestURI().toString(); + } + + + /** + * Configures the given JSESSIONID cookie. + * + * @param cookie The JSESSIONID cookie to be configured + */ + protected void configureSessionCookie(Cookie cookie) { + cookie.setMaxAge(-1); + String contextPath = null; + if (//!connector.getEmptySessionPath() && + (getContext() != null)) { + contextPath = getContext().getEncodedPath(); + } + if ((contextPath != null) && (contextPath.length() > 0)) { + cookie.setPath(contextPath); + } else { + cookie.setPath("/"); + } + if (isSecure()) { + cookie.setSecure(true); + } + } + + + /** + * Return the session associated with this Request, creating one + * if necessary. + */ + public HttpSession getSession() { + return getSession(true); + } + + + public HttpSession getSession(boolean create) { + + // There cannot be a session if no context has been assigned yet + if (context == null) + return (null); + + + // Return the requested session if it exists and is valid + UserSessionManager manager = null; + if (context != null) + manager = context.getManager(); + if (manager == null) + return (null); // Sessions are not supported + + // Return the current session if it exists and is valid + if ((session != null) && !manager.isValid(session)) + session = null; + if (session != null) + return (session); + + + if (requestedSessionId != null) { + try { + session = manager.findSession(requestedSessionId); + } catch (IOException e) { + session = null; + } + if ((session != null) && !manager.isValid(session)) + session = null; + if (session != null) { + manager.access(session); + return (session); + } + } + + // Create a new session if requested and the response is not committed + if (!create) + return (null); + if ((context != null) && (response != null) && + context.getCookies() && + getResponse().isCommitted()) { + throw new IllegalStateException + ("isCommited()"); + } + + // Attempt to reuse session id if one was submitted in a cookie + // Do not reuse the session id if it is from a URL, to prevent possible + // phishing attacks + if (// connector.getEmptySessionPath() && + isRequestedSessionIdFromCookie()) { + session = manager.createSession(getRequestedSessionId()); + } else { + session = manager.createSession(null); + } + + // Creating a new session cookie based on that session + if ((session != null) && (getContext() != null) + && getContext().getCookies()) { + Cookie cookie = new Cookie(ServletRequestImpl.SESSION_COOKIE_NAME, + session.getId()); + configureSessionCookie(cookie); + response.addCookie(cookie); + } + + if (session != null) { + manager.access(session); + return (session); + } else { + return (null); + } + + } + + + /** + * Return the URI converter. + */ +// protected B2CConverter getURIConverter() { +// return URIConverter; +// } +// + + // ------------------------------------------------------ Protected Methods + + + /** + * Parse cookies. + */ + protected void parseCookies() { + + cookiesParsed = true; + + Cookies serverCookies = httpRequest.getCookies(); + int count = serverCookies.getCookieCount(); + if (count <= 0) + return; + + cookies = new Cookie[count]; + + int idx=0; + for (int i = 0; i < count; i++) { + ServerCookie scookie = serverCookies.getCookie(i); + try { + Cookie cookie = new Cookie(scookie.getName().toString(), + scookie.getValue().toString()); + cookie.setPath(scookie.getPath().toString()); + cookie.setVersion(scookie.getVersion()); + String domain = scookie.getDomain().toString(); + if (domain != null) { + cookie.setDomain(scookie.getDomain().toString()); + } + cookies[idx++] = cookie; + } catch(IllegalArgumentException e) { + // Ignore bad cookie + } + } + if( idx < count ) { + Cookie [] ncookies = new Cookie[idx]; + System.arraycopy(cookies, 0, ncookies, 0, idx); + cookies = ncookies; + } + + } + + /** + * Parse request locales. + */ + protected void parseLocales() { + + localesParsed = true; + + Enumeration values = getHeaders("accept-language"); + + while (values.hasMoreElements()) { + String value = values.nextElement().toString(); + parseLocalesHeader(value); + } + + } + + /** + * Parse accept-language header value. + */ + protected void parseLocalesHeader(String value) { + + TreeMap locales = new LocaleParser().parseLocale(value); + // Process the quality values in highest->lowest order (due to + // negating the Double value when creating the key) + Iterator keys = locales.keySet().iterator(); + while (keys.hasNext()) { + Double key = (Double) keys.next(); + ArrayList list = (ArrayList) locales.get(key); + Iterator values = list.iterator(); + while (values.hasNext()) { + Locale locale = (Locale) values.next(); + addLocale(locale); + } + } + + } + + /** + * Parse request parameters. + */ + protected void parseParameters() { + + parametersParsed = true; + + Parameters parameters = httpRequest.getParameters(); + + // getCharacterEncoding() may have been overridden to search for + // hidden form field containing request encoding + String enc = getCharacterEncoding(); + +// boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); + if (enc != null) { + parameters.setEncoding(enc); +// if (useBodyEncodingForURI) { +// parameters.setQueryStringEncoding(enc); +// } + } else { + parameters.setEncoding(DEFAULT_CHARACTER_ENCODING); +// if (useBodyEncodingForURI) { +// parameters.setQueryStringEncoding +// (DEFAULT_CHARACTER_ENCODING); +// } + } + + parameters.handleQueryParameters(); + + if (usingInputStream || usingReader) + return; + + if (!getMethod().equalsIgnoreCase("POST")) + return; + + String contentType = getContentType(); + if (contentType == null) + contentType = ""; + int semicolon = contentType.indexOf(';'); + if (semicolon >= 0) { + contentType = contentType.substring(0, semicolon).trim(); + } else { + contentType = contentType.trim(); + } + if (!("application/x-www-form-urlencoded".equals(contentType))) + return; + + int len = getContentLength(); + + if (len > 0) { +// int maxPostSize = connector.getMaxPostSize(); +// if ((maxPostSize > 0) && (len > maxPostSize)) { +// context.getLogger().info +// (sm.getString("reqB.postTooLarge")); +// throw new IllegalStateException("Post too large"); +// } + try { + byte[] formData = null; + if (len < CACHED_POST_LEN) { + if (postData == null) + postData = new byte[CACHED_POST_LEN]; + formData = postData; + } else { + formData = new byte[len]; + } + int actualLen = readPostBody(formData, len); + if (actualLen == len) { + parameters.processParameters(formData, 0, len); + } + } catch (Throwable t) { + ; // Ignore + } + } + + } + + + /** + * Parse session id in URL. Done in request for performance. + * TODO: should be done in manager + */ + protected void parseSessionCookiesId() { + String sessionCookieName = context.getSessionCookieName(); + + // Parse session id from cookies + Cookies serverCookies = httpRequest.getCookies(); + int count = serverCookies.getCookieCount(); + if (count <= 0) + return; + + for (int i = 0; i < count; i++) { + ServerCookie scookie = serverCookies.getCookie(i); + if (scookie.getName().equals(sessionCookieName)) { + // Override anything requested in the URL + if (!isRequestedSessionIdFromCookie()) { + // Accept only the first session id cookie + //scookie.getValue().convertToAscii(); + + setRequestedSessionId + (scookie.getValue().toString()); + setRequestedSessionCookie(true); + setRequestedSessionURL(false); + } else { + if (!isRequestedSessionIdValid()) { + // Replace the session id until one is valid + //scookie.getValue().convertToAscii(); + setRequestedSessionId + (scookie.getValue().toString()); + } + } + } + } + } + + /** + * Parse session id in URL. + */ + protected void parseSessionId() { + ServletRequestImpl request = this; + ByteChunk uriBC = httpRequest.requestURI().getByteChunk(); + int semicolon = uriBC.indexOf(match, 0, match.length(), 0); + + if (semicolon > 0) { + + // Parse session ID, and extract it from the decoded request URI + int start = uriBC.getStart(); + int end = uriBC.getEnd(); + + int sessionIdStart = semicolon + match.length(); + int semicolon2 = uriBC.indexOf(';', sessionIdStart); + if (semicolon2 >= 0) { + request.setRequestedSessionId + (new String(uriBC.getBuffer(), start + sessionIdStart, + semicolon2 - sessionIdStart)); + // Extract session ID from request URI + byte[] buf = uriBC.getBuffer(); + for (int i = 0; i < end - start - semicolon2; i++) { + buf[start + semicolon + i] + = buf[start + i + semicolon2]; + } + uriBC.setBytes(buf, start, end - start - semicolon2 + semicolon); + } else { + request.setRequestedSessionId + (new String(uriBC.getBuffer(), start + sessionIdStart, + (end - start) - sessionIdStart)); + uriBC.setEnd(start + semicolon); + } + request.setRequestedSessionURL(true); + + } else { + request.setRequestedSessionId(null); + request.setRequestedSessionURL(false); + } + + } + + + /** + * Read post body in an array. + */ + protected int readPostBody(byte body[], int len) + throws IOException { + + int offset = 0; + do { + int inputLen = getStream().read(body, offset, len - offset); + if (inputLen <= 0) { + return offset; + } + offset += inputLen; + } while ((len - offset) > 0); + return len; + + } + + + /** + * Test if a given name is one of the special Servlet-spec SSL attributes. + */ + static boolean isSSLAttribute(String name) { + return ServletRequestImpl.CERTIFICATES_ATTR.equals(name) || + ServletRequestImpl.CIPHER_SUITE_ATTR.equals(name) || + ServletRequestImpl.KEY_SIZE_ATTR.equals(name) || + ServletRequestImpl.SSL_SESSION_ID_ATTR.equals(name); + } + + + public void addAsyncListener(AsyncListener listener) { + } + + + + public void addAsyncListener(AsyncListener listener, + ServletRequest servletRequest, + ServletResponse servletResponse) { + } + + + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + + + @Override + public ServletContext getServletContext() { + return null; + } + + + + @Override + public boolean isAsyncStarted() { + return false; + } + + + + @Override + public boolean isAsyncSupported() { + return false; + } + + + public void setAsyncTimeout(long timeout) { + } + + + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, + ServletResponse servletResponse) + throws IllegalStateException { + return null; + } + + + + @Override + public boolean authenticate(HttpServletResponse response) + throws IOException, ServletException { + return false; + } + + + + @Override + public Part getPart(String name) { + return null; + } + + + + @Override + public void login(String username, String password) throws ServletException { + } + + + + @Override + public void logout() throws ServletException { + } + + + + public long getAsyncTimeout() { + return 0; + } + + + + @Override + public DispatcherType getDispatcherType() { + return null; + } + + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletRequestWrapperImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletRequestWrapperImpl.java new file mode 100644 index 000000000..908b3c6bf --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletRequestWrapperImpl.java @@ -0,0 +1,943 @@ +/* + * 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.servlet; + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpSession; + +import org.apache.tomcat.servlets.session.UserSessionManager; +import org.apache.tomcat.servlets.util.Enumerator; +import org.apache.tomcat.servlets.util.RequestUtil; + + +/** + * Wrapper around a javax.servlet.http.HttpServletRequest + * that transforms an application request object (which might be the original + * one passed to a servlet, or might be based on the 2.3 + * javax.servlet.http.HttpServletRequestWrapper class) + * back into an internal org.apache.catalina.HttpRequest. + *

+ * WARNING: Due to Java's lack of support for multiple + * inheritance, all of the logic in ApplicationRequest is + * duplicated in ApplicationHttpRequest. Make sure that you + * keep these two classes in synchronization when making changes! + * + * @author Craig R. McClanahan + * @author Remy Maucherat + */ +public class ServletRequestWrapperImpl extends HttpServletRequestWrapper { + + + // ------------------------------------------------------- Static Variables + + + /** + * The set of attribute names that are special for request dispatchers. + */ + protected static final String specials[] = + { RequestDispatcherImpl.INCLUDE_REQUEST_URI_ATTR, + RequestDispatcherImpl.INCLUDE_CONTEXT_PATH_ATTR, + RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR, + RequestDispatcherImpl.INCLUDE_PATH_INFO_ATTR, + RequestDispatcherImpl.INCLUDE_QUERY_STRING_ATTR, + RequestDispatcherImpl.FORWARD_REQUEST_URI_ATTR, + RequestDispatcherImpl.FORWARD_CONTEXT_PATH_ATTR, + RequestDispatcherImpl.FORWARD_SERVLET_PATH_ATTR, + RequestDispatcherImpl.FORWARD_PATH_INFO_ATTR, + RequestDispatcherImpl.FORWARD_QUERY_STRING_ATTR }; + + + // ----------------------------------------------------------- Constructors + + + /** + * Construct a new wrapped request around the specified servlet request. + * + * @param request The servlet request being wrapped + */ + public ServletRequestWrapperImpl(HttpServletRequest request, + ServletContextImpl context, + boolean crossContext) { + + super(request); + this.context = context; + this.crossContext = crossContext; + setRequest(request); + + } + + + // ----------------------------------------------------- Instance Variables + + + /** + * The context for this request. + */ + protected ServletContextImpl context = null; + + + /** + * The context path for this request. + */ + protected String contextPath = null; + + + /** + * If this request is cross context, since this changes session accesss + * behavior. + */ + protected boolean crossContext = false; + + + /** + * The current dispatcher type. + */ + protected Object dispatcherType = null; + + + /** + * Descriptive information about this implementation. + */ + protected static final String info = + "org.apache.catalina.core.ApplicationHttpRequest/1.0"; + + + /** + * The request parameters for this request. This is initialized from the + * wrapped request, but updates are allowed. + */ + protected Map parameters = null; + + + /** + * Have the parameters for this request already been parsed? + */ + private boolean parsedParams = false; + + + /** + * The path information for this request. + */ + protected String pathInfo = null; + + + /** + * The query parameters for the current request. + */ + private String queryParamString = null; + + + /** + * The query string for this request. + */ + protected String queryString = null; + + + /** + * The current request dispatcher path. + */ + protected Object requestDispatcherPath = null; + + + /** + * The request URI for this request. + */ + protected String requestURI = null; + + + /** + * The servlet path for this request. + */ + protected String servletPath = null; + + + /** + * The currently active session for this request. + */ + protected HttpSession session = null; + + + /** + * Special attributes. + */ + protected Object[] specialAttributes = new Object[specials.length]; + + + // ------------------------------------------------- ServletRequest Methods + + + /** + * Override the getAttribute() method of the wrapped request. + * + * @param name Name of the attribute to retrieve + */ + public Object getAttribute(String name) { + + if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { + return dispatcherType; + } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { + if ( requestDispatcherPath != null ){ + return requestDispatcherPath.toString(); + } else { + return null; + } + } + + int pos = getSpecial(name); + if (pos == -1) { + return getRequest().getAttribute(name); + } else { + if ((specialAttributes[pos] == null) + && (specialAttributes[5] == null) && (pos >= 5)) { + // If it's a forward special attribute, and null, it means this + // is an include, so we check the wrapped request since + // the request could have been forwarded before the include + return getRequest().getAttribute(name); + } else { + return specialAttributes[pos]; + } + } + + } + + + /** + * Override the getAttributeNames() method of the wrapped + * request. + */ + public Enumeration getAttributeNames() { + return (new AttributeNamesEnumerator()); + } + + + /** + * Override the removeAttribute() method of the + * wrapped request. + * + * @param name Name of the attribute to remove + */ + public void removeAttribute(String name) { + + if (!removeSpecial(name)) + getRequest().removeAttribute(name); + + } + + + /** + * Override the setAttribute() method of the + * wrapped request. + * + * @param name Name of the attribute to set + * @param value Value of the attribute to set + */ + public void setAttribute(String name, Object value) { + + if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { + dispatcherType = value; + return; + } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { + requestDispatcherPath = value; + return; + } + + if (!setSpecial(name, value)) { + getRequest().setAttribute(name, value); + } + + } + + + /** + * Return a RequestDispatcher that wraps the resource at the specified + * path, which may be interpreted as relative to the current request path. + * + * @param path Path of the resource to be wrapped + */ + public RequestDispatcher getRequestDispatcher(String path) { + + if (context == null) + return (null); + + // If the path is already context-relative, just pass it through + if (path == null) + return (null); + else if (path.startsWith("/")) + return (context.getServletContext().getRequestDispatcher(path)); + + // Convert a request-relative path to a context-relative one + String servletPath = + (String) getAttribute(RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR); + if (servletPath == null) + servletPath = getServletPath(); + + // Add the path info, if there is any + String pathInfo = getPathInfo(); + String requestPath = null; + + if (pathInfo == null) { + requestPath = servletPath; + } else { + requestPath = servletPath + pathInfo; + } + + int pos = requestPath.lastIndexOf('/'); + String relative = null; + if (pos >= 0) { + relative = RequestUtil.normalize + (requestPath.substring(0, pos + 1) + path); + } else { + relative = RequestUtil.normalize(requestPath + path); + } + + return (context.getServletContext().getRequestDispatcher(relative)); + + } + + + // --------------------------------------------- HttpServletRequest Methods + + + /** + * Override the getContextPath() method of the wrapped + * request. + */ + public String getContextPath() { + + return (this.contextPath); + + } + + + /** + * Override the getParameter() method of the wrapped request. + * + * @param name Name of the requested parameter + */ + public String getParameter(String name) { + + parseParameters(); + + Object value = parameters.get(name); + if (value == null) + return (null); + else if (value instanceof String[]) + return (((String[]) value)[0]); + else if (value instanceof String) + return ((String) value); + else + return (value.toString()); + + } + + + /** + * Override the getParameterMap() method of the + * wrapped request. + */ + public Map getParameterMap() { + + parseParameters(); + return (parameters); + + } + + + /** + * Override the getParameterNames() method of the + * wrapped request. + */ + public Enumeration getParameterNames() { + + parseParameters(); + return (new Enumerator(parameters.keySet())); + + } + + + /** + * Override the getParameterValues() method of the + * wrapped request. + * + * @param name Name of the requested parameter + */ + public String[] getParameterValues(String name) { + + parseParameters(); + Object value = parameters.get(name); + if (value == null) + return ((String[]) null); + else if (value instanceof String[]) + return ((String[]) value); + else if (value instanceof String) { + String values[] = new String[1]; + values[0] = (String) value; + return (values); + } else { + String values[] = new String[1]; + values[0] = value.toString(); + return (values); + } + + } + + + /** + * Override the getPathInfo() method of the wrapped request. + */ + public String getPathInfo() { + + return (this.pathInfo); + + } + + + /** + * Override the getQueryString() method of the wrapped + * request. + */ + public String getQueryString() { + + return (this.queryString); + + } + + + /** + * Override the getRequestURI() method of the wrapped + * request. + */ + public String getRequestURI() { + + return (this.requestURI); + + } + + + /** + * Override the getRequestURL() method of the wrapped + * request. + */ + public StringBuffer getRequestURL() { + + StringBuffer url = new StringBuffer(); + String scheme = getScheme(); + int port = getServerPort(); + if (port < 0) + port = 80; // Work around java.net.URL bug + + url.append(scheme); + url.append("://"); + url.append(getServerName()); + if ((scheme.equals("http") && (port != 80)) + || (scheme.equals("https") && (port != 443))) { + url.append(':'); + url.append(port); + } + url.append(getRequestURI()); + + return (url); + + } + + + /** + * Override the getServletPath() method of the wrapped + * request. + */ + public String getServletPath() { + + return (this.servletPath); + + } + + + /** + * Return the session associated with this Request, creating one + * if necessary. + */ + public HttpSession getSession() { + return (getSession(true)); + } + + + /** + * Return the session associated with this Request, creating one + * if necessary and requested. + * + * @param create Create a new session if one does not exist + */ + public HttpSession getSession(boolean create) { + + if (crossContext) { + + // There cannot be a session if no context has been assigned yet + if (context == null) + return (null); + UserSessionManager manager = context.getManager(); + // Return the current session if it exists and is valid + if (session != null && manager.isValid(session)) { + return session; + } + + HttpSession other = super.getSession(false); + if (create && (other == null)) { + // First create a session in the first context: the problem is + // that the top level request is the only one which can + // create the cookie safely + other = super.getSession(true); + } + if (other != null) { + HttpSession localSession = null; + try { + localSession = + manager.findSession(other.getId()); + } catch (IOException e) { + // Ignore + } + if (localSession == null && create) { + localSession = + context.getManager().createSession(other.getId()); + } + if (localSession != null) { + context.getManager().access(localSession); + session = localSession; + return session; + } + } + return null; + + } else { + return super.getSession(create); + } + + } + + + /** + * Returns true if the request specifies a JSESSIONID that is valid within + * the context of this ApplicationHttpRequest, false otherwise. + * + * @return true if the request specifies a JSESSIONID that is valid within + * the context of this ApplicationHttpRequest, false otherwise. + */ + public boolean isRequestedSessionIdValid() { + + if (crossContext) { + + String requestedSessionId = getRequestedSessionId(); + if (requestedSessionId == null) + return (false); + if (context == null) + return (false); + UserSessionManager manager = context.getManager(); + if (manager == null) + return (false); + HttpSession session = null; + try { + session = manager.findSession(requestedSessionId); + } catch (IOException e) { + session = null; + } + if ((session != null) && manager.isValid(session)) { + return (true); + } else { + return (false); + } + + } else { + return super.isRequestedSessionIdValid(); + } + } + + + // -------------------------------------------------------- Package Methods + + + /** + * Recycle this request + */ + public void recycle() { + if (session != null) { + context.getManager().endAccess(session); + } + } + + + /** + * Return descriptive information about this implementation. + */ + public String getInfo() { + + return (info); + + } + + + /** + * Perform a shallow copy of the specified Map, and return the result. + * + * @param orig Origin Map to be copied + */ + Map copyMap(Map orig) { + + if (orig == null) + return (new HashMap()); + HashMap dest = new HashMap(); + Iterator keys = orig.keySet().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + dest.put(key, orig.get(key)); + } + return (dest); + + } + + + /** + * Set the context path for this request. + * + * @param contextPath The new context path + */ + void setContextPath(String contextPath) { + + this.contextPath = contextPath; + + } + + + /** + * Set the path information for this request. + * + * @param pathInfo The new path info + */ + void setPathInfo(String pathInfo) { + + this.pathInfo = pathInfo; + + } + + + /** + * Set the query string for this request. + * + * @param queryString The new query string + */ + void setQueryString(String queryString) { + + this.queryString = queryString; + + } + + + /** + * Set the request that we are wrapping. + * + * @param request The new wrapped request + */ + void setRequest(HttpServletRequest request) { + + super.setRequest(request); + + // Initialize the attributes for this request + dispatcherType = request.getAttribute(ServletRequestImpl.DISPATCHER_TYPE_ATTR); + requestDispatcherPath = + request.getAttribute(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR); + + // Initialize the path elements for this request + contextPath = request.getContextPath(); + pathInfo = request.getPathInfo(); + queryString = request.getQueryString(); + requestURI = request.getRequestURI(); + servletPath = request.getServletPath(); + + } + + + /** + * Set the request URI for this request. + * + * @param requestURI The new request URI + */ + void setRequestURI(String requestURI) { + + this.requestURI = requestURI; + + } + + + /** + * Set the servlet path for this request. + * + * @param servletPath The new servlet path + */ + void setServletPath(String servletPath) { + + this.servletPath = servletPath; + + } + + + /** + * Parses the parameters of this request. + * + * If parameters are present in both the query string and the request + * content, they are merged. + */ + void parseParameters() { + + if (parsedParams) { + return; + } + + parameters = new HashMap(); + parameters = copyMap(getRequest().getParameterMap()); + mergeParameters(); + parsedParams = true; + } + + + /** + * Save query parameters for this request. + * + * @param queryString The query string containing parameters for this + * request + */ + void setQueryParams(String queryString) { + this.queryParamString = queryString; + } + + + // ------------------------------------------------------ Protected Methods + + + /** + * Is this attribute name one of the special ones that is added only for + * included servlets? + * + * @param name Attribute name to be tested + */ + protected boolean isSpecial(String name) { + + for (int i = 0; i < specials.length; i++) { + if (specials[i].equals(name)) + return (true); + } + return (false); + + } + + + /** + * Get a special attribute. + * + * @return the special attribute pos, or -1 if it is not a special + * attribute + */ + protected int getSpecial(String name) { + for (int i = 0; i < specials.length; i++) { + if (specials[i].equals(name)) { + return (i); + } + } + return (-1); + } + + + /** + * Set a special attribute. + * + * @return true if the attribute was a special attribute, false otherwise + */ + protected boolean setSpecial(String name, Object value) { + for (int i = 0; i < specials.length; i++) { + if (specials[i].equals(name)) { + specialAttributes[i] = value; + return (true); + } + } + return (false); + } + + + /** + * Remove a special attribute. + * + * @return true if the attribute was a special attribute, false otherwise + */ + protected boolean removeSpecial(String name) { + for (int i = 0; i < specials.length; i++) { + if (specials[i].equals(name)) { + specialAttributes[i] = null; + return (true); + } + } + return (false); + } + + + /** + * Merge the two sets of parameter values into a single String array. + * + * @param values1 First set of values + * @param values2 Second set of values + */ + protected String[] mergeValues(Object values1, Object values2) { + + ArrayList results = new ArrayList(); + + if (values1 == null) + ; + else if (values1 instanceof String) + results.add(values1); + else if (values1 instanceof String[]) { + String values[] = (String[]) values1; + for (int i = 0; i < values.length; i++) + results.add(values[i]); + } else + results.add(values1.toString()); + + if (values2 == null) + ; + else if (values2 instanceof String) + results.add(values2); + else if (values2 instanceof String[]) { + String values[] = (String[]) values2; + for (int i = 0; i < values.length; i++) + results.add(values[i]); + } else + results.add(values2.toString()); + + String values[] = new String[results.size()]; + return ((String[]) results.toArray(values)); + + } + + + // ------------------------------------------------------ Private Methods + + + /** + * Merge the parameters from the saved query parameter string (if any), and + * the parameters already present on this request (if any), such that the + * parameter values from the query string show up first if there are + * duplicate parameter names. + */ + private void mergeParameters() { + + if ((queryParamString == null) || (queryParamString.length() < 1)) + return; + + HashMap queryParameters = new HashMap(); + String encoding = getCharacterEncoding(); + if (encoding == null) + encoding = "ISO-8859-1"; + try { + RequestUtil.parseParameters + (queryParameters, queryParamString, encoding); + } catch (Exception e) { + ; + } + Iterator keys = parameters.keySet().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + Object value = queryParameters.get(key); + if (value == null) { + queryParameters.put(key, parameters.get(key)); + continue; + } + queryParameters.put + (key, mergeValues(value, parameters.get(key))); + } + parameters = queryParameters; + + } + + + // ----------------------------------- AttributeNamesEnumerator Inner Class + + + /** + * Utility class used to expose the special attributes as being available + * as request attributes. + */ + protected class AttributeNamesEnumerator implements Enumeration { + + protected int pos = -1; + protected int last = -1; + protected Enumeration parentEnumeration = null; + protected String next = null; + + public AttributeNamesEnumerator() { + parentEnumeration = getRequest().getAttributeNames(); + for (int i = 0; i < specialAttributes.length; i++) { + if (getAttribute(specials[i]) != null) { + last = i; + } + } + } + + public boolean hasMoreElements() { + return ((pos != last) || (next != null) + || ((next = findNext()) != null)); + } + + public Object nextElement() { + if (pos != last) { + for (int i = pos + 1; i <= last; i++) { + if (getAttribute(specials[i]) != null) { + pos = i; + return (specials[i]); + } + } + } + String result = next; + if (next != null) { + next = findNext(); + } else { + throw new NoSuchElementException(); + } + return result; + } + + protected String findNext() { + String result = null; + while ((result == null) && (parentEnumeration.hasMoreElements())) { + String current = (String) parentEnumeration.nextElement(); + if (!isSpecial(current)) { + result = current; + } + } + return result; + } + + } + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletResponseImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletResponseImpl.java new file mode 100644 index 000000000..eab7af941 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletResponseImpl.java @@ -0,0 +1,1463 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Locale; +import java.util.TimeZone; +import java.util.Vector; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.buf.UEncoder; +import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.HttpResponse; +import org.apache.tomcat.util.http.MimeHeaders; +import org.apache.tomcat.util.http.ServerCookie; + +/** + * Wrapper object for the Coyote response. + * + * @author Remy Maucherat + * @author Craig R. McClanahan + * @version $Revision$ $Date$ + */ + +public class ServletResponseImpl + implements HttpServletResponse { + + /** + * Format for http response header date field + * From DateTool + */ + public static final String HTTP_RESPONSE_DATE_HEADER = + "EEE, dd MMM yyyy HH:mm:ss zzz"; + + // ----------------------------------------------------------- Constructors + + + ServletResponseImpl() { + urlEncoder.addSafeCharacter('/'); + } + + + /** + * The date format we will use for creating date headers. + */ + protected SimpleDateFormat format = null; + + + /** + * The associated output buffer. + */ + protected BodyWriter outputBuffer; + + + /** + * The associated output stream. + */ + protected ServletOutputStreamImpl outputStream; + + + /** + * The associated writer. + */ + protected PrintWriter writer; + + + /** + * The application commit flag. + */ + protected boolean appCommitted = false; + + + /** + * The included flag. + */ + protected boolean included = false; + + + /** + * The characterEncoding flag + */ + private boolean isCharacterEncodingSet = false; + + /** + * The error flag. + */ + protected boolean error = false; + + + /** + * The set of Cookies associated with this Response. + */ + protected ArrayList cookies = new ArrayList(); + + + /** + * Using output stream flag. + */ + protected boolean usingOutputStream = false; + + + /** + * Using writer flag. + */ + protected boolean usingWriter = false; + + + /** + * The request with which this response is associated. + */ + protected ServletRequestImpl req = null; + + /** + * URL encoder. + */ + protected UEncoder urlEncoder = new UEncoder(); + + + /** + * Recyclable buffer to hold the redirect URL. + */ + protected CharChunk redirectURLCC = new CharChunk(1024); + + + private HttpResponse resB; + + + // Cached/derived information - reflected in headers + protected static Locale DEFAULT_LOCALE = Locale.getDefault(); + + public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1"; + + protected Locale locale = DEFAULT_LOCALE; + + // XXX + protected boolean commited = false; + protected String contentType = null; + + /** + * Has the charset been explicitly set. + */ + protected boolean charsetSet = false; + protected String characterEncoding = DEFAULT_CHARACTER_ENCODING; + + + // --------------------------------------------------------- Public Methods + + + /** + * Release all object references, and initialize instance variables, in + * preparation for reuse of this object. + */ + public void recycle() { + + usingOutputStream = false; + usingWriter = false; + appCommitted = false; + commited = false; + included = false; + error = false; + isCharacterEncodingSet = false; + + cookies.clear(); + + outputBuffer.recycle(); + + resB.recycle(); + } + + + // ------------------------------------------------------- Response Methods + + + /** + * Return the number of bytes actually written to the output stream. + */ + public int getContentCount() { + return outputBuffer.getBytesWritten() + outputBuffer.getCharsWritten(); + } + + + /** + * Set the application commit flag. + * + * @param appCommitted The new application committed flag value + */ + public void setAppCommitted(boolean appCommitted) { + this.appCommitted = appCommitted; + } + + + /** + * Application commit flag accessor. + */ + public boolean isAppCommitted() { + return (this.appCommitted || isCommitted() || isSuspended() + || ((getHttpResponse().getContentLength() > 0) + && (getContentCount() >= getHttpResponse().getContentLength()))); + } + + + /** + * Return the "processing inside an include" flag. + */ + public boolean getIncluded() { + return included; + } + + + /** + * Set the "processing inside an include" flag. + * + * @param included true if we are currently inside a + * RequestDispatcher.include(), else false + */ + public void setIncluded(boolean included) { + this.included = included; + } + + + /** + * Return the Request with which this Response is associated. + */ + public ServletRequestImpl getRequest() { + return (this.req); + } + + /** + * Set the Request with which this Response is associated. + * + * @param request The new associated request + */ + public void setRequest(ServletRequestImpl request) { + this.req = (ServletRequestImpl) request; + } + + + /** + * Return the output stream associated with this Response. + */ + public OutputStream getStream() { + return outputStream; + } + + + /** + * Set the output stream associated with this Response. + * + * @param stream The new output stream + */ + public void setStream(OutputStream stream) { + // This method is evil + } + + + /** + * Set the suspended flag. + * + * @param suspended The new suspended flag value + */ + public void setSuspended(boolean suspended) throws IOException { + //coyoteResponse.setCommitted(true); + flushBuffer(); + outputBuffer.setSuspended(suspended); + } + + + /** + * Suspended flag accessor. + */ + public boolean isSuspended() { + return outputBuffer.isSuspended(); + } + + + /** + * Set the error flag. + */ + public void setError() { + error = true; + } + + + /** + * Error flag accessor. + */ + public boolean isError() { + return error; + } + + + /** + * Create and return a ServletOutputStream to write the content + * associated with this Response. + * + * @exception IOException if an input/output error occurs + */ + public ServletOutputStream createOutputStream() + throws IOException { + // Probably useless + return outputStream; + } + + /** + * Return the content type that was set or calculated for this response, + * or null if no content type was set. + */ + public String getContentType() { + String ret = contentType; + + if (ret != null + && characterEncoding != null + && charsetSet) { + ret = ret + ";charset=" + characterEncoding; + } + + return ret; + } + + + /** + * Return a PrintWriter that can be used to render error messages, + * regardless of whether a stream or writer has already been acquired. + * + * @return Writer which can be used for error reports. If the response is + * not an error report returned using sendError or triggered by an + * unexpected exception thrown during the servlet processing + * (and only in that case), null will be returned if the response stream + * has already been used. + * + * @exception IOException if an input/output error occurs + */ + public PrintWriter getReporter() throws IOException { + if (outputBuffer.isNew()) { + outputBuffer.checkConverter(); + if (writer == null) { + writer = new ServletWriterImpl(outputBuffer); + } + return writer; + } else { + return null; + } + } + + + // ------------------------------------------------ ServletResponse Methods + + + /** + * Flush the buffer and commit this response. + * + * @exception IOException if an input/output error occurs + */ + public void flushBuffer() + throws IOException { + outputBuffer.flush(); + } + + + /** + * Return the actual buffer size used for this Response. + */ + public int getBufferSize() { + return outputBuffer.getBufferSize(); + } + + + /** + * Return the character encoding used for this Response. + */ + public String getCharacterEncoding() { + return characterEncoding; + } + + + /** + * Return the servlet output stream associated with this Response. + * + * @exception IllegalStateException if getWriter has + * already been called for this response + * @exception IOException if an input/output error occurs + */ + public ServletOutputStream getOutputStream() + throws IOException { + + if (usingWriter) + throw new IllegalStateException + ("usingWriter"); + + usingOutputStream = true; + return outputStream; + + } + + public BodyWriter getBodyWriter() { + return outputBuffer; + } + + /** + * Return the Locale assigned to this response. + */ + public Locale getLocale() { + return locale; + } + + + /** + * Return the writer associated with this Response. + * + * @exception IllegalStateException if getOutputStream has + * already been called for this response + * @exception IOException if an input/output error occurs + */ + public PrintWriter getWriter() + throws IOException { + + if (usingOutputStream) + throw new IllegalStateException + ("usingOutputStream"); + + /* + * If the response's character encoding has not been specified as + * described in getCharacterEncoding (i.e., the method + * just returns the default value ISO-8859-1), + * getWriter updates it to ISO-8859-1 + * (with the effect that a subsequent call to getContentType() will + * include a charset=ISO-8859-1 component which will also be + * reflected in the Content-Type response header, thereby satisfying + * the Servlet spec requirement that containers must communicate the + * character encoding used for the servlet response's writer to the + * client). + */ + setCharacterEncoding(getCharacterEncoding()); + + usingWriter = true; + outputBuffer.checkConverter(); + if (writer == null) { + writer = new ServletWriterImpl(outputBuffer); + } + return writer; + + } + + + /** + * Has the output of this response already been committed? + */ + public boolean isCommitted() { + return getHttpResponse().isCommitted(); + } + + /** + * Clear any content written to the buffer. + * + * @exception IllegalStateException if this response has already + * been committed + */ + public void reset() { + + if (included) + return; // Ignore any call from an included servlet + + if (isCommitted()) + throw new IllegalStateException("isCommitted"); + + resB.recycle(); // reset headers, status code, message + req.getConnector().reset(this); + contentType = null; + locale = DEFAULT_LOCALE; + characterEncoding = DEFAULT_CHARACTER_ENCODING; + charsetSet = false; + + outputBuffer.reset(); + } + + + /** + * Reset the data buffer but not any status or header information. + * + * @exception IllegalStateException if the response has already + * been committed + */ + public void resetBuffer() { + + if (isCommitted()) + throw new IllegalStateException("isCommitted"); + + outputBuffer.reset(); + + } + + + /** + * Set the buffer size to be used for this Response. + * + * @param size The new buffer size + * + * @exception IllegalStateException if this method is called after + * output has been committed for this response + */ + public void setBufferSize(int size) { + + if (isCommitted() || !outputBuffer.isNew()) + throw new IllegalStateException + ("isCommitted || !isNew"); + + outputBuffer.setBufferSize(size); + + } + + + /** + * Set the content length (in bytes) for this Response. + * Ignored for writers if non-ISO-8859-1 encoding ( we could add more + * encodings that are constant. + */ + public void setContentLength(int length) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + // writers can use variable-length encoding. + if (usingWriter && !"ISO-8859-1".equals(getCharacterEncoding())) { + return; + } + getHttpResponse().setContentLength(length); + + } + + + /** + * Set the content type for this Response. + * + * @param type The new content type + */ + public void setContentType(String type) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + // Ignore charset if getWriter() has already been called + if (usingWriter) { + if (type != null) { + int index = type.indexOf(";"); + if (index != -1) { + type = type.substring(0, index); + } + } + } + + getHttpResponse().setContentType(type); + + // Check to see if content type contains charset + if (type != null) { + int index = type.indexOf(";"); + if (index != -1) { + int len = type.length(); + index++; + while (index < len && Character.isSpace(type.charAt(index))) { + index++; + } + if (index+7 < len + && type.charAt(index) == 'c' + && type.charAt(index+1) == 'h' + && type.charAt(index+2) == 'a' + && type.charAt(index+3) == 'r' + && type.charAt(index+4) == 's' + && type.charAt(index+5) == 'e' + && type.charAt(index+6) == 't' + && type.charAt(index+7) == '=') { + isCharacterEncodingSet = true; + } + } + } + } + + + /* + * Overrides the name of the character encoding used in the body + * of the request. This method must be called prior to reading + * request parameters or reading input using getReader(). + * + * @param charset String containing the name of the chararacter encoding. + */ + public void setCharacterEncoding(String charset) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + // Ignore any call made after the getWriter has been invoked + // The default should be used + if (usingWriter) + return; + + if (isCommitted()) + return; + if (charset == null) + return; + + characterEncoding = charset; + charsetSet=true; + isCharacterEncodingSet = true; + } + + + + /** + * Set the Locale that is appropriate for this response, including + * setting the appropriate character encoding. + * + * @param locale The new locale + */ + public void setLocale(Locale locale) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + if (locale == null) { + return; // throw an exception? + } + + // Save the locale for use by getLocale() + this.locale = locale; + + // Set the contentLanguage for header output + String contentLanguage = locale.getLanguage(); + if ((contentLanguage != null) && (contentLanguage.length() > 0)) { + String country = locale.getCountry(); + StringBuffer value = new StringBuffer(contentLanguage); + if ((country != null) && (country.length() > 0)) { + value.append('-'); + value.append(country); + } + contentLanguage = value.toString(); + } + resB.setHeader("Content-Language", contentLanguage); + + // Ignore any call made after the getWriter has been invoked. + // The default should be used + if (usingWriter) + return; + + if (isCharacterEncodingSet) { + return; + } + + Locale2Charset cm = req.getContext().getCharsetMapper(); + String charset = cm.getCharset( locale ); + if ( charset != null ){ + setCharacterEncoding(charset); + } + + } + + + // --------------------------------------------------- HttpResponse Methods + + + /** + * Return an array of all cookies set for this response, or + * a zero-length array if no cookies have been set. + */ + public Cookie[] getCookies() { + return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()])); + } + + + /** + * Return the value for the specified header, or null if this + * header has not been set. If more than one value was added for this + * name, only the first is returned; use getHeaderValues() to retrieve all + * of them. + * + * @param name Header name to look up + */ + public String getHeader(String name) { + return getHttpResponse().getMimeHeaders().getHeader(name); + } + + + /** + * Return an array of all the header names set for this response, or + * a zero-length array if no headers have been set. + */ + public Collection getHeaderNames() { + + MimeHeaders headers = getHttpResponse().getMimeHeaders(); + int n = headers.size(); + ArrayList result = new ArrayList(); + for (int i = 0; i < n; i++) { + result.add(headers.getName(i).toString()); + } + return result; + } + + + /** + * Return an array of all the header values associated with the + * specified header name, or an zero-length array if there are no such + * header values. + * + * @param name Header name to look up + */ + public String[] getHeaderValues(String name) { + + Enumeration enumeration = getHttpResponse().getMimeHeaders().values(name); + Vector result = new Vector(); + while (enumeration.hasMoreElements()) { + result.addElement(enumeration.nextElement()); + } + String[] resultArray = new String[result.size()]; + result.copyInto(resultArray); + return resultArray; + + } + + + /** + * Return the error message that was set with sendError() + * for this Response. + */ + public String getMessage() { + return getHttpResponse().getMessage(); + } + + + /** + * Return the HTTP status code associated with this Response. + */ + public int getStatus() { + return getHttpResponse().getStatus(); + } + + + /** + * Reset this response, and specify the values for the HTTP status code + * and corresponding message. + * + * @exception IllegalStateException if this response has already been + * committed + */ + public void reset(int status, String message) { + reset(); + setStatus(status, message); + } + + + // -------------------------------------------- HttpServletResponse Methods + + + /** + * Add the specified Cookie to those that will be included with + * this Response. + * + * @param cookie Cookie to be added + */ + public void addCookie(final Cookie cookie) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + cookies.add(cookie); + + final StringBuffer sb = new StringBuffer(); + ServerCookie.appendCookieValue + (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(), + cookie.getPath(), cookie.getDomain(), cookie.getComment(), + cookie.getMaxAge(), cookie.getSecure(), false); + + // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 ) + // RFC2965 is not supported by browsers and the Servlet spec + // asks for 2109. + addHeader("Set-Cookie", sb.toString()); + + } + + + /** + * Add the specified date header to the specified value. + * + * @param name Name of the header to set + * @param value Date value to be set + */ + public void addDateHeader(String name, long value) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) { + return; + } + + if (format == null) { + format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER, + Locale.US); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + addHeader(name, FastHttpDateFormat.formatDate(value, format)); + + } + + + /** + * Add the specified header to the specified value. + * + * @param name Name of the header to set + * @param value Value to be set + */ + public void addHeader(String name, String value) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + getHttpResponse().addHeader(name, value); + + } + + + /** + * Add the specified integer header to the specified value. + * + * @param name Name of the header to set + * @param value Integer value to be set + */ + public void addIntHeader(String name, int value) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + addHeader(name, "" + value); + + } + + + /** + * Has the specified header been set already in this response? + * + * @param name Name of the header to check + */ + public boolean containsHeader(String name) { + // Need special handling for Content-Type and Content-Length due to + // special handling of these in coyoteResponse + char cc=name.charAt(0); + if(cc=='C' || cc=='c') { + if(name.equalsIgnoreCase("Content-Type")) { + // Will return null if this has not been set + return getContentType() != null; + } + if(name.equalsIgnoreCase("Content-Length")) { + // -1 means not known and is not sent to client + return (getHttpResponse().getContentLength() != -1); + } + } + + return getHttpResponse().containsHeader(name); + } + + + /** + * Encode the session identifier associated with this response + * into the specified redirect URL, if necessary. + * + * @param url URL to be encoded + */ + public String encodeRedirectURL(String url) { + + if (isEncodeable(toAbsolute(url))) { + return (toEncoded(url, req.getSession().getId())); + } else { + return (url); + } + + } + + + /** + * Encode the session identifier associated with this response + * into the specified redirect URL, if necessary. + * + * @param url URL to be encoded + * + * @deprecated As of Version 2.1 of the Java Servlet API, use + * encodeRedirectURL() instead. + */ + public String encodeRedirectUrl(String url) { + return (encodeRedirectURL(url)); + } + + + /** + * Encode the session identifier associated with this response + * into the specified URL, if necessary. + * + * @param url URL to be encoded + */ + public String encodeURL(String url) { + + String absolute = toAbsolute(url); + if (isEncodeable(absolute)) { + // W3c spec clearly said + if (url.equalsIgnoreCase("")){ + url = absolute; + } + return (toEncoded(url, req.getSession().getId())); + } else { + return (url); + } + + } + + + /** + * Encode the session identifier associated with this response + * into the specified URL, if necessary. + * + * @param url URL to be encoded + * + * @deprecated As of Version 2.1 of the Java Servlet API, use + * encodeURL() instead. + */ + public String encodeUrl(String url) { + return (encodeURL(url)); + } + + + /** + * Send an acknowledgment of a request. + * + * @exception IOException if an input/output error occurs + */ + public void sendAcknowledgement() + throws IOException { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + req.getConnector().acknowledge(this); + } + + + /** + * Send an error response with the specified status and a + * default message. + * + * @param status HTTP status code to send + * + * @exception IllegalStateException if this response has + * already been committed + * @exception IOException if an input/output error occurs + */ + public void sendError(int status) + throws IOException { + sendError(status, null); + } + + + /** + * Send an error response with the specified status and message. + * + * @param status HTTP status code to send + * @param message Corresponding message to send + * + * @exception IllegalStateException if this response has + * already been committed + * @exception IOException if an input/output error occurs + */ + public void sendError(int status, String message) + throws IOException { + + if (isCommitted()) + throw new IllegalStateException + ("isCommitted"); + + // Ignore any call from an included servlet + if (included) + return; + + setError(); + + getHttpResponse().setStatus(status); + getHttpResponse().setMessage(message); + + // Clear any data content that has been buffered + resetBuffer(); + + // Cause the response to be finished (from the application perspective) + String statusPage = req.getContext().findStatusPage(status); + + if (statusPage != null) { + req.getContext().handleStatusPage(req, this, status, statusPage); + } else { + // Send a default message body. + // TODO: maybe other mechanism to customize default. + defaultStatusPage(status, message); + } + setSuspended(true); + } + + /** + * Default handler for status code != 200 + */ + void defaultStatusPage(int status, String message) + throws IOException { + setContentType("text/html"); + if (status > 400 && status < 600) { + if (getOutputBuffer().getBytesWritten() == 0) { + getOutputBuffer().write("

Status: " + + status + "

Message: " + message + + "

"); + getOutputBuffer().flush(); + } + } + } + + + + /** + * Send a temporary redirect to the specified redirect location URL. + * + * @param location Location URL to redirect to + * + * @exception IllegalStateException if this response has + * already been committed + * @exception IOException if an input/output error occurs + */ + public void sendRedirect(String location) + throws IOException { + + if (isCommitted()) + throw new IllegalStateException + ("isCommitted"); + + // Ignore any call from an included servlet + if (included) + return; + + // Clear any data content that has been buffered + resetBuffer(); + + // Generate a temporary redirect to the specified location + try { + String absolute = toAbsolute(location); + setStatus(SC_FOUND); + setHeader("Location", absolute); + } catch (IllegalArgumentException e) { + setStatus(SC_NOT_FOUND); + } + + // Cause the response to be finished (from the application perspective) + setSuspended(true); + + } + + + /** + * Set the specified date header to the specified value. + * + * @param name Name of the header to set + * @param value Date value to be set + */ + public void setDateHeader(String name, long value) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) { + return; + } + + if (format == null) { + format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER, + Locale.US); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + setHeader(name, FastHttpDateFormat.formatDate(value, format)); + + } + + + /** + * Set the specified header to the specified value. + * + * @param name Name of the header to set + * @param value Value to be set + */ + public void setHeader(String name, String value) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + getHttpResponse().setHeader(name, value); + + } + + + /** + * Set the specified integer header to the specified value. + * + * @param name Name of the header to set + * @param value Integer value to be set + */ + public void setIntHeader(String name, int value) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + setHeader(name, "" + value); + + } + + + /** + * Set the HTTP status to be returned with this response. + * + * @param status The new HTTP status + */ + public void setStatus(int status) { + setStatus(status, null); + } + + + /** + * Set the HTTP status and message to be returned with this response. + * + * @param status The new HTTP status + * @param message The associated text message + * + * @deprecated As of Version 2.1 of the Java Servlet API, this method + * has been deprecated due to the ambiguous meaning of the message + * parameter. + */ + public void setStatus(int status, String message) { + + if (isCommitted()) + return; + + // Ignore any call from an included servlet + if (included) + return; + + getHttpResponse().setStatus(status); + getHttpResponse().setMessage(message); + + } + + + // ------------------------------------------------------ Protected Methods + + + /** + * Return true if the specified URL should be encoded with + * a session identifier. This will be true if all of the following + * conditions are met: + *
    + *
  • The request we are responding to asked for a valid session + *
  • The requested session ID was not received via a cookie + *
  • The specified URL points back to somewhere within the web + * application that is responding to this request + *
+ * + * @param location Absolute URL to be validated + */ + protected boolean isEncodeable(final String location) { + + if (location == null) + return (false); + + // Is this an intra-document reference? + if (location.startsWith("#")) + return (false); + + // Are we in a valid session that is not using cookies? + final ServletRequestImpl hreq = req; + final HttpSession session = hreq.getSession(false); + if (session == null) + return (false); + if (hreq.isRequestedSessionIdFromCookie()) + return (false); + + // Is this a valid absolute URL? + URL url = null; + try { + url = new URL(location); + } catch (MalformedURLException e) { + return (false); + } + + // Does this URL match down to (and including) the context path? + if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol())) + return (false); + if (!hreq.getServerName().equalsIgnoreCase(url.getHost())) + return (false); + int serverPort = hreq.getServerPort(); + if (serverPort == -1) { + if ("https".equals(hreq.getScheme())) + serverPort = 443; + else + serverPort = 80; + } + int urlPort = url.getPort(); + if (urlPort == -1) { + if ("https".equals(url.getProtocol())) + urlPort = 443; + else + urlPort = 80; + } + if (serverPort != urlPort) + return (false); + + String contextPath = req.getContext().getContextPath(); + if (contextPath != null) { + String file = url.getFile(); + if ((file == null) || !file.startsWith(contextPath)) + return (false); + if( file.indexOf(";jsessionid=" + session.getId()) >= 0 ) + return (false); + } + + // This URL belongs to our web application, so it is encodeable + return (true); + + } + + + /** + * Convert (if necessary) and return the absolute URL that represents the + * resource referenced by this possibly relative URL. If this URL is + * already absolute, return it unchanged. + * + * @param location URL to be (possibly) converted and then returned + * + * @exception IllegalArgumentException if a MalformedURLException is + * thrown when converting the relative URL to an absolute one + */ + private String toAbsolute(String location) { + + if (location == null) + return (location); + + boolean leadingSlash = location.startsWith("/"); + + if (leadingSlash || !hasScheme(location)) { + + redirectURLCC.recycle(); + + String scheme = req.getScheme(); + String name = req.getServerName(); + int port = req.getServerPort(); + + try { + redirectURLCC.append(scheme, 0, scheme.length()); + redirectURLCC.append("://", 0, 3); + redirectURLCC.append(name, 0, name.length()); + if ((scheme.equals("http") && port != 80) + || (scheme.equals("https") && port != 443)) { + redirectURLCC.append(':'); + String portS = port + ""; + redirectURLCC.append(portS, 0, portS.length()); + } + if (!leadingSlash) { + String relativePath = req.getDecodedRequestURI(); + int pos = relativePath.lastIndexOf('/'); + relativePath = relativePath.substring(0, pos); + + String encodedURI = null; + encodedURI = urlEncoder.encodeURL(relativePath); + redirectURLCC.append(encodedURI, 0, encodedURI.length()); + redirectURLCC.append('/'); + } + redirectURLCC.append(location, 0, location.length()); + } catch (IOException e) { + IllegalArgumentException iae = + new IllegalArgumentException(location); + iae.initCause(e); + throw iae; + } + + return redirectURLCC.toString(); + + } else { + + return (location); + + } + + } + + + /** + * Determine if a URI string has a scheme component. + */ + private boolean hasScheme(String uri) { + int len = uri.length(); + for(int i=0; i < len ; i++) { + char c = uri.charAt(i); + if(c == ':') { + return i > 0; + } else if(!isSchemeChar(c)) { + return false; + } + } + return false; + } + + /** + * Determine if the character is allowed in the scheme of a URI. + * See RFC 2396, Section 3.1 + */ + private static boolean isSchemeChar(char c) { + return Character.isLetterOrDigit(c) || + c == '+' || c == '-' || c == '.'; + } + + + /** + * Return the specified URL with the specified session identifier + * suitably encoded. + * + * @param url URL to be encoded with the session id + * @param sessionId Session id to be included in the encoded URL + */ + protected String toEncoded(String url, String sessionId) { + + if ((url == null) || (sessionId == null)) + return (url); + + String path = url; + String query = ""; + String anchor = ""; + int question = url.indexOf('?'); + if (question >= 0) { + path = url.substring(0, question); + query = url.substring(question); + } + int pound = path.indexOf('#'); + if (pound >= 0) { + anchor = path.substring(pound); + path = path.substring(0, pound); + } + StringBuffer sb = new StringBuffer(path); + if( sb.length() > 0 ) { // jsessionid can't be first. + sb.append(";jsessionid="); + sb.append(sessionId); + } + sb.append(anchor); + sb.append(query); + return (sb.toString()); + + } + + + public int getBytesWritten() { + return outputBuffer.getBytesWritten(); + } + + public BodyWriter getOutputBuffer() { + return outputBuffer; + } + + public void setWriter(BodyWriter ob) { + outputBuffer = ob; + outputStream = new ServletOutputStreamImpl(outputBuffer); + } + + public CharSequence getResponseHeader(String name) { + MessageBytes v = getHttpResponse().getMimeHeaders().getValue(name); + return (v == null) ? null : v.toString(); + } + + + public HttpResponse getHttpResponse() { + return resB; + } + + + public void setHttpResponse(HttpResponse resB, BodyWriter ob) { + this.resB = resB; + setWriter(ob); + } + + + + @Override + public Collection getHeaders(String name) { + return null; + } + + +} + diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletResponseIncludeWrapper.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletResponseIncludeWrapper.java new file mode 100644 index 000000000..4c10b647a --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletResponseIncludeWrapper.java @@ -0,0 +1,116 @@ +/* + * 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.servlet; + + +import java.io.IOException; +import java.util.Locale; + +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + + +/** + * Wrapper around the response object received as parameter to + * RequestDispatcher.include(). + * + * @author Costin Manolache + */ +public class ServletResponseIncludeWrapper extends HttpServletResponseWrapper { + public ServletResponseIncludeWrapper(ServletResponse current) { + super((HttpServletResponse) current); + } + + // Not overriden: + /* + public boolean containsHeader(String name) + public String encodeRedirectUrl(String url) + public String encodeRedirectURL(String url) + public String encodeUrl(String url) + public String encodeURL(String url) + public void flushBuffer() throws IOException + public int getBufferSize() + public String getCharacterEncoding() + public String getContentType() + public Locale getLocale() + public ServletOutputStream getOutputStream() throws IOException + public ServletResponse getResponse() + public PrintWriter getWriter() throws IOException + public boolean isCommitted() + public void resetBuffer() + public void setCharacterEncoding(String charset) + public void setResponse(ServletResponse response) + */ + + public void reset() { + if (getResponse().isCommitted()) + getResponse().reset(); + else + throw new IllegalStateException(); + } + + public void setContentLength(int len) { + } + + public void setContentType(String type) { + } + + public void setLocale(Locale loc) { + } + + public void setBufferSize(int size) { + } + + public void addCookie(Cookie cookie) { + } + + public void addDateHeader(String name, long value) { + } + + public void addHeader(String name, String value) { + } + + public void addIntHeader(String name, int value) { + } + + public void sendError(int sc) throws IOException { + } + + public void sendError(int sc, String msg) throws IOException { + } + + public void sendRedirect(String location) throws IOException { + } + + public void setDateHeader(String name, long value) { + } + + public void setHeader(String name, String value) { + } + + public void setIntHeader(String name, int value) { + } + + public void setStatus(int sc) { + } + + public void setStatus(int sc, String msg) { + } +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletWriterImpl.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletWriterImpl.java new file mode 100644 index 000000000..82e3e2069 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/ServletWriterImpl.java @@ -0,0 +1,289 @@ +/* + * 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.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +/** + * Coyote implementation of the servlet writer. + * + * @author Remy Maucherat + */ +public class ServletWriterImpl + extends PrintWriter { + + + // -------------------------------------------------------------- Constants + + + private static final char[] LINE_SEP = { '\r', '\n' }; + + + // ----------------------------------------------------- Instance Variables + + + protected Writer ob; + protected boolean error = false; + + + // ----------------------------------------------------------- Constructors + + + public ServletWriterImpl(Writer ob) { + super(ob); + this.ob = ob; + } + + + // --------------------------------------------------------- Public Methods + + + /** + * Prevent cloning the facade. + */ + protected Object clone() + throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + + // -------------------------------------------------------- Package Methods + + + /** + * Clear facade. + */ + void clear() { + ob = null; + } + + + /** + * Recycle. + */ + void recycle() { + error = false; + } + + + // --------------------------------------------------------- Writer Methods + + + public void flush() { + + if (error) + return; + + try { + ob.flush(); + } catch (IOException e) { + error = true; + } + + } + + + public void close() { + + // We don't close the PrintWriter - super() is not called, + // so the stream can be reused. We close ob. + try { + ob.close(); + } catch (IOException ex ) { + ; + } + error = false; + + } + + + public boolean checkError() { + flush(); + return error; + } + + + public void write(int c) { + + if (error) + return; + + try { + ob.write(c); + } catch (IOException e) { + error = true; + } + + } + + + public void write(char buf[], int off, int len) { + + if (error) + return; + + try { + ob.write(buf, off, len); + } catch (IOException e) { + error = true; + } + + } + + + public void write(char buf[]) { + write(buf, 0, buf.length); + } + + + public void write(String s, int off, int len) { + + if (error) + return; + + try { + ob.write(s, off, len); + } catch (IOException e) { + error = true; + } + + } + + + public void write(String s) { + write(s, 0, s.length()); + } + + + // ---------------------------------------------------- PrintWriter Methods + + + public void print(boolean b) { + if (b) { + write("true"); + } else { + write("false"); + } + } + + + public void print(char c) { + write(c); + } + + + public void print(int i) { + write(String.valueOf(i)); + } + + + public void print(long l) { + write(String.valueOf(l)); + } + + + public void print(float f) { + write(String.valueOf(f)); + } + + + public void print(double d) { + write(String.valueOf(d)); + } + + + public void print(char s[]) { + write(s); + } + + + public void print(String s) { + if (s == null) { + s = "null"; + } + write(s); + } + + + public void print(Object obj) { + write(String.valueOf(obj)); + } + + + public void println() { + write(LINE_SEP); + } + + + public void println(boolean b) { + print(b); + println(); + } + + + public void println(char c) { + print(c); + println(); + } + + + public void println(int i) { + print(i); + println(); + } + + + public void println(long l) { + print(l); + println(); + } + + + public void println(float f) { + print(f); + println(); + } + + + public void println(double d) { + print(d); + println(); + } + + + public void println(char c[]) { + print(c); + println(); + } + + + public void println(String s) { + print(s); + println(); + } + + + public void println(Object o) { + print(o); + println(); + } + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/TomcatLite.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/TomcatLite.java new file mode 100644 index 000000000..87e04b8b0 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/TomcatLite.java @@ -0,0 +1,646 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.servlet.Filter; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tomcat.integration.ObjectManager; +import org.apache.tomcat.integration.simple.SimpleObjectManager; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.buf.UriNormalizer; +import org.apache.tomcat.util.http.mapper.MappingData; + +/** + * Simpler, lower footprint serlvet engine. + * + * Uses ObjectManager to integate with an embedding app + * - the object names it uses: + * + * Internal objects created by Tomcat and registered for management + * and injection: + * - Servlet:CONTEXT_PATH:SERVLETNAME - a ServletWrapper + * - ServletContext:CONTEXT_PATH + * - ProtocolHandler:ep-PORT - coyote ProtocolHandler + * - CoyoteServer:CoyoteServer-PORT + * - CoyoteAdapter:PATH - for the coyote used Adapter + * - TomcatLite - this object. + * - Connector - the connector object + * + * Plugins to be constructed by framework ( defaults set in initDefaults ): + * - UserSessionManager + * - UserTemplateClassMapper + * - ContextPreinitListener + * - Connector + * - WebappServletMapper + * - WebappFilterMapper + * - default-servlet + * - jspwildcard-servlet + * - Foo-servlet - servlet named Foo + * + * + * @author Costin Manolache + */ +public class TomcatLite implements Runnable { + + private String serverDirName; + private File workDir; + + // all contexts - hostMapper knows about hostnames and how they are mapped. + // this shouldn't be needed if we want to delegate ctx management + private ArrayList contexts = new ArrayList(); + + URLClassLoader contextParentLoader; + + // Discovered or default Host/Context mapper + Filter hostMapper; + + // Servlets to preload in each context, configurable from CLI or API + Map preloadServlets = new HashMap(); + Map preloadMappings = new HashMap(); + + Map ctxDefaultInitParam = new HashMap(); + + Connector connector; + + ObjectManager om; + + static String SERVLETS_PACKAGE = "org.apache.tomcat.servlets"; + + + protected boolean daemon = false; + + public TomcatLite() { + } + + public TomcatLite(ObjectManager om) { + this.setObjectManager(om); + } + + // --------------- start/stop --------------- + + public static ObjectManager defaultObjectManager() { + SimpleObjectManager cfg = new SimpleObjectManager(); + cfg.loadResource("org/apache/coyote/servlet/config.properties"); + return cfg; + } + /** + * Return the object manager associated with this tomcat. + * If none set, create a minimal one with the default + * values. + */ + public ObjectManager getObjectManager() { + if (om == null) { + om = defaultObjectManager(); + } + return om; + } + + public void setObjectManager(ObjectManager om) { + this.om = om; + } + + public List/**/ getWebapps() { + return contexts; + } + + public URLClassLoader getContextParentLoader() { + if (contextParentLoader == null) { + + ClassLoader parent = this.getClass().getClassLoader(); + contextParentLoader = new URLClassLoader(new URL[] {}, + parent); + + /*if (engineRepo == null) { + engineRepo = new Repository(); + engineRepo.setParentClassLoader(parent); + } + + contextParentLoader = + engineRepo.getClassLoader(); + */ + } + return contextParentLoader; + } + + public void start() throws IOException { + long t0 = System.currentTimeMillis(); + + // start all contexts + // init all contexts + Iterator i1 = contexts.iterator(); + while (i1.hasNext()) { + ServletContextImpl ctx = (ServletContextImpl) i1.next(); + try { + ctx.start(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + long t1 = System.currentTimeMillis(); + System.err.println("Engine.start() " + (t1-t0)); + } + + + /** + * Add a context - used for IntrospectionUtils. + * + * ContextPath:ContextBaseDir + */ + public void setContext(String c) throws ServletException { + String[] pathDir = c.split(":", 2); + addServletContext("", pathDir[1], pathDir[0]); + } + + public void setServletContexts(List c) throws ServletException { + for (ServletContext ctx: c) { + addServletContext((ServletContextImpl) ctx); + } + } + + public void setPreload(String servletNameClass) { + String[] nv = servletNameClass.split(":"); + preloadServlets.put(nv[0], nv[1]); + } + + public void addPreload(String servletName, String servletClassName) { + preloadServlets.put(servletName, servletClassName); + } + + public void setDefaultInitParam(String nameValue) { + String[] nv = nameValue.split(":"); + ctxDefaultInitParam.put(nv[0], nv[1]); + } + + public void addDefaultInitParam(String name, String value) { + ctxDefaultInitParam.put(name, value); + } + + public void setPreloadMappings(String servletPath) { + String[] nv = servletPath.split(":"); + preloadMappings.put(nv[0], nv[1]); + } + + public void addPreloadMapping(String servletName, String path) { + preloadMappings.put(servletName, path); + } + + public void stop() { + Iterator i1 = contexts.iterator(); + while (i1.hasNext()) { + ServletContextImpl ctx = (ServletContextImpl) i1.next(); + try { + ctx.destroy(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + try { + stopConnector(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // -------------- Context add/remove -------------- + + public static String[] DEFAULT_WELCOME = { "index.html" }; + + public void addServletContext(ServletContextImpl ctx) throws ServletException { + ctx.setTomcat(this); + if (hostMapper == null) { + hostMapper = new WebappContextMapper(); + } + + ((WebappContextMapper) hostMapper).addHost(ctx.getHostname(), null); + ((WebappContextMapper) hostMapper).addContext(ctx.getHostname(), + ctx); + + contexts.add(ctx); + + getObjectManager().bind("ServletContext:" + ctx.getContextPath(), + ctx); + + } + + /** + * Add a context. + * + * web.xml will be read as part of init, and the initialization will be + * part of start or lazy. + * + * @param hostname - "" + * if default host, or string to be matched with Host header + * @param basePath = directory where the webapp is installed + * @param path - + * context path, "/" for root, "/examples", etc + * @return a servlet context + * @throws ServletException + */ + public ServletContext addServletContext(String hostname, + String basePath, + String path) + throws ServletException + { + ServletContextImpl ctx = new ServletContextImpl(); + ctx.setContextPath(path); + ctx.setBasePath(basePath); + addServletContext(ctx); + return ctx; + } + + public void removeServletContext(ServletContext sctx) + throws ServletException + { + ServletContextImpl ctx = (ServletContextImpl) sctx; + // TODO: destroy all servlets and filters + // TODO: clean up any other reference to the context or its loader + notifyRemove(ctx); + } + + + /** + * Required for ServletContext.getContext(uri); + * @throws ServletException + * @throws IOException + */ + public ServletContextImpl getContext(ServletContextImpl impl, String uri) + throws IOException, ServletException { + // Create a request - needs to be simplified + ServletRequestImpl req = createMessage(impl, impl.contextPath, uri); + hostMapper.doFilter(req, null, null); + return req.getContext(); + } + + public ServletResponseImpl service(ServletRequestImpl req) throws IOException, Exception { + ServletResponseImpl res = req.getResponse(); + service(req, res); + endRequest(req, res); + return res; + } + + public void service(ServletRequestImpl req, ServletResponseImpl res) + throws Exception, IOException { + // parse the session id from URI + req.parseSessionId(); + + try { + UriNormalizer.decodeRequest(req.getHttpRequest().decodedURI(), + req.getHttpRequest().requestURI(), + req.getHttpRequest().getURLDecoder()); + } catch(IOException ioe) { + res.setStatus(400); + return; + } + + MappingData mapRes = req.getMappingData(); + try { + // TODO: make hostMapper configurable, implement interface, + // simple to set on ROOT context + hostMapper.doFilter(req, null, null); + + + ServletContextImpl ctx = (ServletContextImpl)mapRes.context; + if( ctx == null ) { + // TODO: 404 + res.setStatus(404); + return; + } + req.setContext(ctx); + + // bind class loader + Thread.currentThread().setContextClassLoader(ctx.getClassLoader()); + + WebappServletMapper mapper = ctx.getMapper(); + mapper.map(req.getHttpRequest().decodedURI(), mapRes); + + // Possible redirect + MessageBytes redirectPathMB = mapRes.redirectPath; + if (!redirectPathMB.isNull()) { + String redirectPath = res.urlEncoder.encodeURL(redirectPathMB.toString()); + String query = req.getQueryString(); + if (req.isRequestedSessionIdFromURL()) { + // This is not optimal, but as this is not very common, it + // shouldn't matter + redirectPath = redirectPath + ";" + ServletRequestImpl.SESSION_PARAMETER_NAME + "=" + + req.getRequestedSessionId(); + } + if (query != null) { + // This is not optimal, but as this is not very common, it + // shouldn't matter + redirectPath = redirectPath + "?" + query; + } + res.sendRedirect(redirectPath); + return; + } + + req.parseSessionCookiesId(); + + ServletConfigImpl h=(ServletConfigImpl)mapRes.wrapper; + if (h != null) { + req.setWrapper((ServletConfigImpl)mapRes.wrapper); + serviceServlet(ctx, req, res, h, mapRes ); + // send the response... + + //res.flushBuffer(); + + // Recycle the wrapper request and response + //req.recycle(); + //res.recycle(); + } + } finally { + if(mapRes != null ) + mapRes.recycle(); + } + } + + /** Coyote / mapper adapter. Result of the mapper. + * + * This replaces the valve chain, the path is: + * 1. coyote calls mapper -> result Adapter + * 2. service is called. Additional filters are set on the wrapper. + * @param mapRes + */ + private void serviceServlet(ServletContextImpl ctx, + ServletRequestImpl req, + ServletResponseImpl res, + ServletConfigImpl servletConfig, + MappingData mapRes) + throws IOException { + Servlet servlet = null; + try { + if (servletConfig.isUnavailable()) { + handleUnavailable(res, servletConfig); + return; + } + try { + servlet = servletConfig.allocate(); + } catch(ServletException ex) { + handleUnavailable(res, servletConfig); + } + WebappFilterMapper filterMap = ctx.getFilterMapper(); + FilterChainImpl chain = + filterMap.createFilterChain(req, servletConfig, servlet); + + try { + if (chain == null) { + if (servlet != null) { + servlet.service(req, res); + } else { + System.err.println("No servlet " + req.getRequestURI()); + res.sendError(404); + } + } else { + chain.doFilter(req, res); + } + } catch(UnavailableException ex) { + servletConfig.unavailable(ex); + handleUnavailable(res, servletConfig); + return; + } + + // servlet completed without exception. Check status + int status = res.getStatus(); + if (status != 200 && !res.isCommitted()) { + String statusPage = ctx.findStatusPage(status); + + if (statusPage != null) { + ctx.handleStatusPage(req, res, status, statusPage); + } else { + // Send a default message body. + // TODO: maybe other mechanism to customize default. + res.defaultStatusPage(status, res.getMessage()); + } + } + } catch (Throwable t) { + ctx.handleError(req, res, t); + } finally { + if (servlet != null) { + servletConfig.deallocate(servlet); + } + } + } + + private void handleUnavailable(ServletResponseImpl response, + ServletConfigImpl servletConfig) + throws IOException { + long available = servletConfig.getAvailable(); + if ((available > 0L) && (available < Long.MAX_VALUE)) + response.setDateHeader("Retry-After", available); + // TODO: handle via error pages ! + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, + "Service unavailable"); + } + + + // ------------ Notifications for JMX ---------------- + + void notifyAdd(Object o) { + } + + void notifyRemove(Object o) { + } + + public void setServerDir(String dir) { + this.serverDirName = dir; + } + + public File getWork() { + if (workDir == null) { + if (serverDirName == null) { + serverDirName = "./"; + } + File rootDirFile = new File(serverDirName); + workDir = new File(rootDirFile, "tomcat-work"); + if (workDir.exists()) { + workDir.mkdirs(); + } + } + return workDir; + } + + /** + * Init + * + * @throws ServletException + * @throws IOException + */ + public void init() throws ServletException, IOException { + getObjectManager().bind("TomcatLite", this); + if (contexts.size() == 0) { + setContext("/:./webapps/ROOT"); + } + getConnector().setObjectManager(getObjectManager()); + Iterator i1 = contexts.iterator(); + while (i1.hasNext()) { + ServletContextImpl ctx = (ServletContextImpl) i1.next(); + try { + ctx.init(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + /** + * Initialize an webapp and add it to the server. + * - load web.xml + * - call + * + * @param rootDir + * @param path + * @param deployServlet + */ + public void init(String rootDir, String path) + throws ServletException, IOException { + + long t0 = System.currentTimeMillis(); + + ServletContextImpl ctx = + (ServletContextImpl)addServletContext(null, + rootDir, + path); + ctx.init(); + + long t1 = System.currentTimeMillis(); + + // At this point all config is loaded. Contexts are not yet init() + // - this will happen on start. + System.err.println("Context.init() " + path + " " + (t1-t0)); + } + + /** + * Get an empty request/response pair ( response available + * as getResponse() ). Optional set input and output buffers. + * + * This can be used for a connector-less interface to tomcat lite. + * + * TODO: make it independent of coyote ! + */ + + public ServletRequestImpl createMessage() { + ServletRequestImpl req = new ServletRequestImpl(); + ServletResponseImpl res = req.getResponse(); + + getConnector().initRequest(req, res); + + req.getHttpRequest().method().setString("GET"); + req.getHttpRequest().protocol().setString("HTTP/1.1"); + + return req; + } + + /** + * Used internally for mapping. + */ + private ServletRequestImpl createMessage(ServletContextImpl deployCtx, + String ctxPath, + String reqPath) { + ServletRequestImpl req = createMessage(); + req.setContextPath(ctxPath); + req.setContext(deployCtx); + req.setRequestURI(ctxPath + reqPath); + return req; + } + + + /** + * Set a global filter that will be used for context mapping. + * + * The filter will get a request, with requestUri and hostname set. + * + * It needs to compute the context path and set it as an attribute. + * + * Advanced features may include on-demand loading of webapps, large scale + * virtual hosting, etc. + */ + public void setContextMapper(Filter hostMapper2) { + this.hostMapper = hostMapper2; + } + + public void endRequest(ServletRequestImpl req, + ServletResponseImpl res) throws IOException { + res.outputBuffer.flush(); + req.getConnector().finishResponse(res); + } + + public Connector getConnector() { + if (connector == null) { + connector = (Connector) getObjectManager().get(Connector.class); + setConnector(connector); + } + return connector; + } + + public void setConnector(Connector c) { + connector = c; + connector.setTomcatLite(this); + getObjectManager().bind("Connector", connector); + } + + + public void setDaemon(boolean d) { + getConnector(); + if (connector != null) { + connector.setDaemon(d); + } + } + + public void startConnector() throws IOException { + getConnector(); + if (connector != null) { + connector.start(); + } + } + + public void stopConnector() throws Exception { + if (connector != null) { + connector.stop(); + } + } + + public void run() { + try { + execute(); + } catch (ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void execute() throws ServletException, IOException { + init(); + start(); + startConnector(); + } +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappContextMapper.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappContextMapper.java new file mode 100644 index 000000000..d9abbd8e5 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappContextMapper.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.servlet; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.mapper.MappingData; + +/** + * This handles host and context mapping. + * + * The default implementation for tomcat lite is very limitted - + * no support for virtual hosts, no support for contexts deeper than + * 1 level. The intention is to override this with more advanced mappers. + * + * With 'ConfigurableHosts' interface it is possible for a smart + * mapper to load/unload virtual hosts at runtime, maybe from a + * database. It should be possible to use databases to store huge number + * of hosts or webapps. + * + */ +public class WebappContextMapper implements Filter { + + ServletContext rootContext; + Map contexts = new HashMap(); + + public WebappContextMapper() { + } + + public void addHost(String name, String[] aliases) { + } + + /** + * Add a new Context to an existing Host. + * + * @param hostName Virtual host name this context belongs to + * @param contextPath Context path + * @param context Context object + * @param welcomeResources Welcome files defined for this context + * @param resources Static resources of the context + */ + public void addContext(String hostName, + ServletContext context) + throws ServletException + { + String path = context.getContextPath(); + if (path.lastIndexOf("/") > 0) { + throw new ServletException("Base context mapper supports only one level"); + } + if ("/".equals(path)) { + rootContext = context; + } + MessageBytes mb = MessageBytes.newInstance(); + mb.setChars(path.toCharArray(), 0, path.length()); + contexts.put(mb.toString(), context); + } + + + /** + * Remove a context from an existing host. + * + * @param hostName Virtual host name this context belongs to + * @param path Context path + */ + public void removeContext(String hostName, String path) + throws ServletException { + if ("/".equals(path)) { + rootContext = null; + } + contexts.remove(path); + } + + /** + * Map the specified URI. + */ + private void mapContext(ServletRequestImpl req) + throws IOException, ServletException { + MessageBytes uriMB = req.getDecodedRequestURIMB(); + MappingData mappingData = req.getMappingData(); + uriMB.toChars(); + CharChunk uri = uriMB.getCharChunk(); + + + if (uri.length() < 2 || contexts.size() == 0) { + mappingData.context = rootContext; + if (rootContext != null) { + mappingData.contextPath.setString(rootContext.getContextPath()); + } + return; + } + + int nextSlash = uri.indexOf('/', 1); + if (nextSlash == -1) { + nextSlash = uri.length(); + } + mappingData.contextPath.setChars(uri.getChars(), 0, nextSlash); + ServletContext servletContext = contexts.get(mappingData.contextPath.toString()); + + if (servletContext != null) { + mappingData.context = servletContext; + } else { + mappingData.context = rootContext; + if (rootContext != null) { + mappingData.contextPath.setString(rootContext.getContextPath()); + } + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void doFilter(ServletRequest request, + ServletResponse response, + FilterChain chain) + throws IOException, ServletException { + ServletRequestImpl req = (ServletRequestImpl)request; + mapContext(req); + } + + public void destroy() { + } +} \ No newline at end of file diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappFilterMapper.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappFilterMapper.java new file mode 100644 index 000000000..305b245c4 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappFilterMapper.java @@ -0,0 +1,533 @@ +/* + * 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.servlet; + + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.apache.tomcat.servlets.util.RequestUtil; + +/** + * First filter after the context and servlet are mapped. It will add + * web.xml-defined filters. + * + * costin: This is another mapping - done in RequestDispatcher or initial + * mapping. + * Also: StandardHostValve - sets attribute for error pages, + * StandardWrapperValve - mapping per invocation + * + * @author Greg Murray + * @author Remy Maucherat + */ +public class WebappFilterMapper implements Filter { + + + // -------------------------------------------------------------- Constants + + + public static final int ERROR = 1; + public static final Integer ERROR_INTEGER = new Integer(ERROR); + public static final int FORWARD = 2; + public static final Integer FORWARD_INTEGER = new Integer(FORWARD); + public static final int INCLUDE = 4; + public static final Integer INCLUDE_INTEGER = new Integer(INCLUDE); + public static final int REQUEST = 8; + public static final Integer REQUEST_INTEGER = new Integer(REQUEST); + + /** + * Request dispatcher state. + */ + public static final String DISPATCHER_TYPE_ATTR = + "org.apache.catalina.core.DISPATCHER_TYPE"; + + /** + * Request dispatcher path. + */ + public static final String DISPATCHER_REQUEST_PATH_ATTR = + "org.apache.catalina.core.DISPATCHER_REQUEST_PATH"; + + + // ----------------------------------------------------------- Constructors + ServletContextImpl servletContext; + + public WebappFilterMapper() { + } + + public WebappFilterMapper(ServletContextImpl impl) { + servletContext = impl; + } + + public void setServletContext(ServletContextImpl sc) { + servletContext = sc; + } + + // --------------------------------------------------------- Public Methods + + ArrayList filterMaps = new ArrayList(); + + public void addMapping(String filterName, + String url, + String servletName, + String type[], boolean isMatchAfter) { + FilterMap map = new FilterMap(); + map.setURLPattern(url); + map.setFilterName(filterName); + map.setServletName(servletName); + if (isMatchAfter) { + filterMaps.add(map); + } else { + filterMaps.add(0, map); + } + } + + /** + * Construct and return a FilterChain implementation that will wrap the + * execution of the specified servlet instance. If we should not execute + * a filter chain at all, return null. + * + * @param request The servlet request we are processing + * @param servlet The servlet instance to be wrapped + */ + public FilterChainImpl createFilterChain(ServletRequest request, + ServletConfigImpl wrapper, + Servlet servlet) { + + // If there is no servlet to execute, return null + if (servlet == null) + return (null); + + // get the dispatcher type + int dispatcher = -1; + if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) { + Integer dispatcherInt = + (Integer) request.getAttribute(DISPATCHER_TYPE_ATTR); + dispatcher = dispatcherInt.intValue(); + } + String requestPath = null; + Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR); + + if (attribute != null){ + requestPath = attribute.toString(); + } + + HttpServletRequest hreq = null; + if (request instanceof HttpServletRequest) + hreq = (HttpServletRequest)request; + + // Create and initialize a filter chain object + FilterChainImpl filterChain = null; + if ((request instanceof ServletRequestImpl)) { + ServletRequestImpl req = (ServletRequestImpl) request; + filterChain = (FilterChainImpl) req.getFilterChain(); + filterChain.release(); + } else { + // Security: Do not recycle + filterChain = new FilterChainImpl(); + } + + filterChain.setServlet(wrapper, servlet); + + // If there are no filter mappings, we are done + if ((filterMaps.size() == 0)) + return (filterChain); + + // Acquire the information we will need to match filter mappings + String servletName = wrapper.getServletName(); + + int n = 0; + + // TODO(costin): optimize: separate in 2 lists, one for url-mapped, one for + // servlet-name. Maybe even separate list for dispatcher and + // non-dispatcher + + // TODO(costin): optimize: set the FilterConfig in the FilterMap, to + // avoid second hash lookup + + // Add the relevant path-mapped filters to this filter chain + for (int i = 0; i < filterMaps.size(); i++) { + FilterMap filterMap = (FilterMap)filterMaps.get(i); + if (!matchDispatcher(filterMap ,dispatcher)) { + continue; + } + if (!matchFiltersURL(filterMap, requestPath)) + continue; + FilterConfigImpl filterConfig = + servletContext.getFilter(filterMap.getFilterName()); + if (filterConfig == null) { + // FIXME - log configuration problem + continue; + } + filterChain.addFilter(filterConfig); + n++; + } + + // Add filters that match on servlet name second + for (int i = 0; i < filterMaps.size(); i++) { + FilterMap filterMap = (FilterMap)filterMaps.get(i); + if (!matchDispatcher(filterMap ,dispatcher)) { + continue; + } + if (!matchFiltersServlet(filterMap, servletName)) + continue; + FilterConfigImpl filterConfig = + servletContext.getFilter(filterMap.getFilterName()); + if (filterConfig == null) { + ; // FIXME - log configuration problem + continue; + } + filterChain.addFilter(filterConfig); + n++; + } + + // Return the completed filter chain + return (filterChain); + + } + + + // -------------------------------------------------------- Private Methods + + + /** + * Return true if the context-relative request path + * matches the requirements of the specified filter mapping; + * otherwise, return null. + * + * @param filterMap Filter mapping being checked + * @param requestPath Context-relative request path of this request + */ + private boolean matchFiltersURL(FilterMap filterMap, String requestPath) { + + if (requestPath == null) + return (false); + + // Match on context relative request path + String testPath = filterMap.getURLPattern(); + if (testPath == null) + return (false); + + // Case 1 - Exact Match + if (testPath.equals(requestPath)) + return (true); + + // Case 2 - Path Match ("/.../*") + if (testPath.equals("/*")) + return (true); + if (testPath.endsWith("/*")) { + if (testPath.regionMatches(0, requestPath, 0, + testPath.length() - 2)) { + if (requestPath.length() == (testPath.length() - 2)) { + return (true); + } else if ('/' == requestPath.charAt(testPath.length() - 2)) { + return (true); + } + } + return (false); + } + + // Case 3 - Extension Match + if (testPath.startsWith("*.")) { + int slash = requestPath.lastIndexOf('/'); + int period = requestPath.lastIndexOf('.'); + if ((slash >= 0) && (period > slash) + && (period != requestPath.length() - 1) + && ((requestPath.length() - period) + == (testPath.length() - 1))) { + return (testPath.regionMatches(2, requestPath, period + 1, + testPath.length() - 2)); + } + } + + // Case 4 - "Default" Match + return (false); // NOTE - Not relevant for selecting filters + + } + + + /** + * Return true if the specified servlet name matches + * the requirements of the specified filter mapping; otherwise + * return false. + * + * @param filterMap Filter mapping being checked + * @param servletName Servlet name being checked + */ + private boolean matchFiltersServlet(FilterMap filterMap, + String servletName) { + + if (servletName == null) { + return (false); + } else { + if (servletName.equals(filterMap.getServletName())) { + return (true); + } else { + return false; + } + } + + } + + + /** + * Convienience method which returns true if the dispatcher type + * matches the dispatcher types specified in the FilterMap + */ + private boolean matchDispatcher(FilterMap filterMap, int dispatcher) { + switch (dispatcher) { + case FORWARD : { + if (filterMap.getDispatcherMapping() == FilterMap.FORWARD || + filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR || + filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) { + return true; + } + break; + } + case INCLUDE : { + if (filterMap.getDispatcherMapping() == FilterMap.INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR || + filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) { + return true; + } + break; + } + case REQUEST : { + if (filterMap.getDispatcherMapping() == FilterMap.REQUEST || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE) { + return true; + } + break; + } + case ERROR : { + if (filterMap.getDispatcherMapping() == FilterMap.ERROR || + filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR || + filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR || + filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE || + filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE) { + return true; + } + break; + } + } + return false; + } + + + // -------------------- Map elements ----------------------- + + public static class FilterMap implements Serializable { + + + // ------------------------------------------------------------- Properties + + + /** + * The name of this filter to be executed when this mapping matches + * a particular request. + */ + + public static final int ERROR = 1; + public static final int FORWARD = 2; + public static final int FORWARD_ERROR =3; + public static final int INCLUDE = 4; + public static final int INCLUDE_ERROR = 5; + public static final int INCLUDE_ERROR_FORWARD =6; + public static final int INCLUDE_FORWARD = 7; + public static final int REQUEST = 8; + public static final int REQUEST_ERROR = 9; + public static final int REQUEST_ERROR_FORWARD = 10; + public static final int REQUEST_ERROR_FORWARD_INCLUDE = 11; + public static final int REQUEST_ERROR_INCLUDE = 12; + public static final int REQUEST_FORWARD = 13; + public static final int REQUEST_INCLUDE = 14; + public static final int REQUEST_FORWARD_INCLUDE= 15; + + // represents nothing having been set. This will be seen + // as equal to a REQUEST + private static final int NOT_SET = -1; + + private int dispatcherMapping=NOT_SET; + + private String filterName = null; + + /** + * The URL pattern this mapping matches. + */ + private String urlPattern = null; + + /** + * The servlet name this mapping matches. + */ + private String servletName = null; + + + + public String getFilterName() { + return (this.filterName); + } + + public void setFilterName(String filterName) { + this.filterName = filterName; + } + + + public String getServletName() { + return (this.servletName); + } + + public void setServletName(String servletName) { + this.servletName = servletName; + } + + + public String getURLPattern() { + return (this.urlPattern); + } + + public void setURLPattern(String urlPattern) { + this.urlPattern = RequestUtil.URLDecode(urlPattern); + } + + /** + * + * This method will be used to set the current state of the FilterMap + * representing the state of when filters should be applied: + * + * ERROR + * FORWARD + * FORWARD_ERROR + * INCLUDE + * INCLUDE_ERROR + * INCLUDE_ERROR_FORWARD + * REQUEST + * REQUEST_ERROR + * REQUEST_ERROR_INCLUDE + * REQUEST_ERROR_FORWARD_INCLUDE + * REQUEST_INCLUDE + * REQUEST_FORWARD, + * REQUEST_FORWARD_INCLUDE + * + */ + public void setDispatcher(String dispatcherString) { + String dispatcher = dispatcherString.toUpperCase(); + + if (dispatcher.equals("FORWARD")) { + + // apply FORWARD to the global dispatcherMapping. + switch (dispatcherMapping) { + case NOT_SET : dispatcherMapping = FORWARD; break; + case ERROR : dispatcherMapping = FORWARD_ERROR; break; + case INCLUDE : dispatcherMapping = INCLUDE_FORWARD; break; + case INCLUDE_ERROR : dispatcherMapping = INCLUDE_ERROR_FORWARD; break; + case REQUEST : dispatcherMapping = REQUEST_FORWARD; break; + case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_FORWARD; break; + case REQUEST_ERROR_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; + case REQUEST_INCLUDE : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break; + } + } else if (dispatcher.equals("INCLUDE")) { + // apply INCLUDE to the global dispatcherMapping. + switch (dispatcherMapping) { + case NOT_SET : dispatcherMapping = INCLUDE; break; + case ERROR : dispatcherMapping = INCLUDE_ERROR; break; + case FORWARD : dispatcherMapping = INCLUDE_FORWARD; break; + case FORWARD_ERROR : dispatcherMapping = INCLUDE_ERROR_FORWARD; break; + case REQUEST : dispatcherMapping = REQUEST_INCLUDE; break; + case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_INCLUDE; break; + case REQUEST_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; + case REQUEST_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break; + } + } else if (dispatcher.equals("REQUEST")) { + // apply REQUEST to the global dispatcherMapping. + switch (dispatcherMapping) { + case NOT_SET : dispatcherMapping = REQUEST; break; + case ERROR : dispatcherMapping = REQUEST_ERROR; break; + case FORWARD : dispatcherMapping = REQUEST_FORWARD; break; + case FORWARD_ERROR : dispatcherMapping = REQUEST_ERROR_FORWARD; break; + case INCLUDE : dispatcherMapping = REQUEST_INCLUDE; break; + case INCLUDE_ERROR : dispatcherMapping = REQUEST_ERROR_INCLUDE; break; + case INCLUDE_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break; + case INCLUDE_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; + } + } else if (dispatcher.equals("ERROR")) { + // apply ERROR to the global dispatcherMapping. + switch (dispatcherMapping) { + case NOT_SET : dispatcherMapping = ERROR; break; + case FORWARD : dispatcherMapping = FORWARD_ERROR; break; + case INCLUDE : dispatcherMapping = INCLUDE_ERROR; break; + case INCLUDE_FORWARD : dispatcherMapping = INCLUDE_ERROR_FORWARD; break; + case REQUEST : dispatcherMapping = REQUEST_ERROR; break; + case REQUEST_INCLUDE : dispatcherMapping = REQUEST_ERROR_INCLUDE; break; + case REQUEST_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD; break; + case REQUEST_FORWARD_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; + } + } + } + + public int getDispatcherMapping() { + // per the SRV.6.2.5 absence of any dispatcher elements is + // equivelant to a REQUEST value + if (dispatcherMapping == NOT_SET) return REQUEST; + else return dispatcherMapping; + } + + } + + + public void init(FilterConfig filterConfig) throws ServletException { + } + + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) + throws IOException, ServletException { + } + + + public void destroy() { + } + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappServletMapper.java b/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappServletMapper.java new file mode 100644 index 000000000..8e2f307f8 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/WebappServletMapper.java @@ -0,0 +1,883 @@ +/* + * 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.servlet; + +import java.io.File; +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.apache.tomcat.util.buf.Ascii; +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.mapper.MappingData; + +/** + * Mapper, which implements the servlet API mapping rules (which are derived + * from the HTTP rules). + * + * Based on catalina mapper - but simplified. All host and context mappings + * is done in HostMapper - this is just dealing with web.xml. + * + * For corner cases ( very large number of rules, dynamic rules, etc ) you + * can override the mapper for a context with a class extending this. + * + * TODO: remove, use coyote-level mapper or user-space + */ +public class WebappServletMapper implements Filter { + + /** + * Context associated with this wrapper, used for wrapper mapping. + */ + public ContextMapElement contextMapElement = new ContextMapElement(); + + + // --------------------------------------------------------- Public Methods + public WebappServletMapper() { + } + + public void setServletContext(ServletContextImpl impl) { + contextMapElement.object = impl; + contextMapElement.name = impl.getContextPath(); + } + + + /** Set context, used for wrapper mapping (request dispatcher). + * + * @param welcomeResources Welcome files defined for this context + * @param resources Static resources of the context + */ + public void setContext(String path, String[] welcomeResources, + File resources) { + contextMapElement.name = path; + contextMapElement.welcomeResources = welcomeResources; + contextMapElement.resources = resources; + } + + + /** + * Add a wrapper to the context associated with this wrapper. + * + * @param path Wrapper mapping + * @param wrapper The Wrapper object + */ + public void addWrapper(String path, Object wrapper) { + addWrapper(contextMapElement, path, wrapper); + } + + + public void addWrapper(String path, Object wrapper, boolean jspWildCard) { + addWrapper(contextMapElement, path, wrapper, jspWildCard); + } + + + public void addWrapper(ContextMapElement context, String path, Object wrapper) { + addWrapper(context, path, wrapper, false); + } + + + /** + * Adds a wrapper to the given context. + * + * @param context The context to which to add the wrapper + * @param path Wrapper mapping + * @param wrapper The Wrapper object + * @param jspWildCard true if the wrapper corresponds to the JspServlet + * and the mapping path contains a wildcard; false otherwise + */ + protected void addWrapper(ContextMapElement context, String path, Object wrapper, + boolean jspWildCard) { + + synchronized (context) { + WrapperMapElement newWrapper = new WrapperMapElement(); + newWrapper.object = wrapper; + newWrapper.jspWildCard = jspWildCard; + if (path.endsWith("/*")) { + // Wildcard wrapper + newWrapper.name = path.substring(0, path.length() - 2); + WrapperMapElement[] oldWrappers = context.wildcardWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length + 1]; + if (insertMap(oldWrappers, newWrappers, newWrapper)) { + context.wildcardWrappers = newWrappers; + int slashCount = slashCount(newWrapper.name); + if (slashCount > context.nesting) { + context.nesting = slashCount; + } + } + } else if (path.startsWith("*.")) { + // Extension wrapper + newWrapper.name = path.substring(2); + WrapperMapElement[] oldWrappers = context.extensionWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length + 1]; + if (insertMap(oldWrappers, newWrappers, newWrapper)) { + context.extensionWrappers = newWrappers; + } + } else if (path.equals("/")) { + // Default wrapper + newWrapper.name = ""; + context.defaultWrapper = newWrapper; + } else { + // Exact wrapper + newWrapper.name = path; + WrapperMapElement[] oldWrappers = context.exactWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length + 1]; + if (insertMap(oldWrappers, newWrappers, newWrapper)) { + context.exactWrappers = newWrappers; + } + } + } + } + + + /** + * Remove a wrapper from the context associated with this wrapper. + * + * @param path Wrapper mapping + */ + public void removeWrapper(String path) { + removeWrapper(contextMapElement, path); + } + + + protected void removeWrapper(ContextMapElement context, String path) { + synchronized (context) { + if (path.endsWith("/*")) { + // Wildcard wrapper + String name = path.substring(0, path.length() - 2); + WrapperMapElement[] oldWrappers = context.wildcardWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length - 1]; + if (removeMap(oldWrappers, newWrappers, name)) { + // Recalculate nesting + context.nesting = 0; + for (int i = 0; i < newWrappers.length; i++) { + int slashCount = slashCount(newWrappers[i].name); + if (slashCount > context.nesting) { + context.nesting = slashCount; + } + } + context.wildcardWrappers = newWrappers; + } + } else if (path.startsWith("*.")) { + // Extension wrapper + String name = path.substring(2); + WrapperMapElement[] oldWrappers = context.extensionWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length - 1]; + if (removeMap(oldWrappers, newWrappers, name)) { + context.extensionWrappers = newWrappers; + } + } else if (path.equals("/")) { + // Default wrapper + context.defaultWrapper = null; + } else { + // Exact wrapper + String name = path; + WrapperMapElement[] oldWrappers = context.exactWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length - 1]; + if (removeMap(oldWrappers, newWrappers, name)) { + context.exactWrappers = newWrappers; + } + } + } + } + + /** + * Map the specified URI relative to the context, + * mutating the given mapping data. + * + * @param uri URI + * @param mappingData This structure will contain the result of the mapping + * operation + */ + public void map(MessageBytes uri, MappingData mappingData) + throws Exception { + + uri.toChars(); + CharChunk uricc = uri.getCharChunk(); + //uricc.setLimit(-1); + internalMapWrapper(contextMapElement, uricc, mappingData); + + } + + + // -------------------------------------------------------- Private Methods + + + /** + * Wrapper mapping. + */ + private final void internalMapWrapper(ContextMapElement context, + CharChunk path, + MappingData mappingData) + throws Exception { + + int pathOffset = path.getOffset(); + int pathEnd = path.getEnd(); + int servletPath = pathOffset; + boolean noServletPath = false; + + int length = context.name.length(); + if (length == 1) length--; + if (length != (pathEnd - pathOffset)) { + servletPath = pathOffset + length; + } else { + noServletPath = true; + // What is this doing ??? + path.append('/'); + pathOffset = path.getOffset(); + pathEnd = path.getEnd(); + servletPath = pathOffset+length; + } + + path.setOffset(servletPath); + + // Rule 1 -- Exact Match + WrapperMapElement[] exactWrappers = context.exactWrappers; + internalMapExactWrapper(exactWrappers, path, mappingData); + + // Rule 2 -- Prefix Match + boolean checkJspWelcomeFiles = false; + WrapperMapElement[] wildcardWrappers = context.wildcardWrappers; + if (mappingData.wrapper == null) { + internalMapWildcardWrapper(wildcardWrappers, context.nesting, + path, mappingData); + if (mappingData.wrapper != null && mappingData.jspWildCard) { + char[] buf = path.getBuffer(); + if (buf[pathEnd - 1] == '/') { + /* + * Path ending in '/' was mapped to JSP servlet based on + * wildcard match (e.g., as specified in url-pattern of a + * jsp-property-group. + * Force the context's welcome files, which are interpreted + * as JSP files (since they match the url-pattern), to be + * considered. See Bugzilla 27664. + */ + mappingData.wrapper = null; + checkJspWelcomeFiles = true; + } else { + // See Bugzilla 27704 + mappingData.wrapperPath.setChars(buf, path.getStart(), + path.getLength()); + mappingData.pathInfo.recycle(); + } + } + } + + if(mappingData.wrapper == null && noServletPath) { + // The path is empty, redirect to "/" + mappingData.redirectPath.setChars + (path.getBuffer(), pathOffset, pathEnd); + path.setEnd(pathEnd - 1); + return; + } + + // Rule 3 -- Extension Match + WrapperMapElement[] extensionWrappers = context.extensionWrappers; + if (mappingData.wrapper == null && !checkJspWelcomeFiles) { + internalMapExtensionWrapper(extensionWrappers, path, mappingData); + } + + File file = null; + // Rule 4 -- Welcome resources processing for servlets + if (mappingData.wrapper == null) { + boolean checkWelcomeFiles = checkJspWelcomeFiles; + if (!checkWelcomeFiles) { + char[] buf = path.getBuffer(); + checkWelcomeFiles = (buf[pathEnd - 1] == '/'); + } + if (checkWelcomeFiles) { + for (int i = 0; (i < context.welcomeResources.length) + && (mappingData.wrapper == null); i++) { + path.setOffset(pathOffset); + path.setEnd(pathEnd); + path.append(context.welcomeResources[i], 0, + context.welcomeResources[i].length()); + path.setOffset(servletPath); + + // Rule 4a -- Welcome resources processing for exact macth + internalMapExactWrapper(exactWrappers, path, mappingData); + + // Rule 4b -- Welcome resources processing for prefix match + if (mappingData.wrapper == null) { + internalMapWildcardWrapper + (wildcardWrappers, context.nesting, + path, mappingData); + } + + // Rule 4c -- Welcome resources processing + // for physical folder + if (mappingData.wrapper == null + && context.resources != null) { + // Default servlet: check if it's file or dir to apply + // welcome files rules. + // TODO: Save the File in attributes, + // to avoid duplication in DefaultServlet. + + String pathStr = path.toString(); + file = new File(context.resources, pathStr); + if (file.exists() && !(file.isDirectory()) ) { + + internalMapExtensionWrapper(extensionWrappers, + path, mappingData); + if (mappingData.wrapper == null + && context.defaultWrapper != null) { + mappingData.wrapper = + context.defaultWrapper.object; + mappingData.requestPath.setChars + (path.getBuffer(), path.getStart(), + path.getLength()); + mappingData.wrapperPath.setChars + (path.getBuffer(), path.getStart(), + path.getLength()); + mappingData.requestPath.setString(pathStr); + mappingData.wrapperPath.setString(pathStr); + } + } + } + } + + path.setOffset(servletPath); + path.setEnd(pathEnd); + } + + } + + + // Rule 7 -- Default servlet + if (mappingData.wrapper == null && !checkJspWelcomeFiles) { + if (context.defaultWrapper != null) { + mappingData.wrapper = context.defaultWrapper.object; + mappingData.requestPath.setChars + (path.getBuffer(), path.getStart(), path.getLength()); + mappingData.wrapperPath.setChars + (path.getBuffer(), path.getStart(), path.getLength()); + } + // Redirection to a folder + char[] buf = path.getBuffer(); + if (context.resources != null && buf[pathEnd -1 ] != '/') { + String pathStr = path.toString(); + file = new File( context.resources, pathStr); + if (file.exists() && file.isDirectory()) { + // Note: this mutates the path: do not do any processing + // after this (since we set the redirectPath, there + // shouldn't be any) + path.setOffset(pathOffset); + path.append('/'); + mappingData.redirectPath.setChars + (path.getBuffer(), path.getStart(), path.getLength()); + } else { + mappingData.requestPath.setString(pathStr); + mappingData.wrapperPath.setString(pathStr); + } + } + } + + path.setOffset(pathOffset); + path.setEnd(pathEnd); + } + + + /** + * Exact mapping. + */ + private final void internalMapExactWrapper + (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) { + int pos = find(wrappers, path); + if ((pos != -1) && (path.equals(wrappers[pos].name))) { + mappingData.requestPath.setString(wrappers[pos].name); + mappingData.wrapperPath.setString(wrappers[pos].name); + mappingData.wrapper = wrappers[pos].object; + } + } + + + /** + * Wildcard mapping. + */ + private final void internalMapWildcardWrapper + (WrapperMapElement[] wrappers, int nesting, CharChunk path, + MappingData mappingData) { + + int pathEnd = path.getEnd(); + int pathOffset = path.getOffset(); + + int lastSlash = -1; + int length = -1; + int pos = find(wrappers, path); + if (pos != -1) { + boolean found = false; + while (pos >= 0) { + if (path.startsWith(wrappers[pos].name)) { + length = wrappers[pos].name.length(); + if (path.getLength() == length) { + found = true; + break; + } else if (path.startsWithIgnoreCase("/", length)) { + found = true; + break; + } + } + if (lastSlash == -1) { + lastSlash = nthSlash(path, nesting + 1); + } else { + lastSlash = lastSlash(path); + } + path.setEnd(lastSlash); + pos = find(wrappers, path); + } + path.setEnd(pathEnd); + if (found) { + mappingData.wrapperPath.setString(wrappers[pos].name); + if (path.getLength() > length) { + mappingData.pathInfo.setChars + (path.getBuffer(), + path.getOffset() + length, + path.getLength() - length); + } + mappingData.requestPath.setChars + (path.getBuffer(), path.getOffset(), path.getLength()); + mappingData.wrapper = wrappers[pos].object; + mappingData.jspWildCard = wrappers[pos].jspWildCard; + } + } + } + + + /** + * Extension mappings. + */ + private final void internalMapExtensionWrapper + (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) { + char[] buf = path.getBuffer(); + int pathEnd = path.getEnd(); + int servletPath = path.getOffset(); + int slash = -1; + for (int i = pathEnd - 1; i >= servletPath; i--) { + if (buf[i] == '/') { + slash = i; + break; + } + } + if (slash == -1 ) slash = 0; + if (slash >= 0) { + int period = -1; + for (int i = pathEnd - 1; i > slash; i--) { + if (buf[i] == '.') { + period = i; + break; + } + } + if (period >= 0) { + path.setOffset(period + 1); + path.setEnd(pathEnd); + int pos = find(wrappers, path); + if ((pos != -1) + && (path.equals(wrappers[pos].name))) { + mappingData.wrapperPath.setChars + (buf, servletPath, pathEnd - servletPath); + mappingData.requestPath.setChars + (buf, servletPath, pathEnd - servletPath); + mappingData.wrapper = wrappers[pos].object; + } + path.setOffset(servletPath); + path.setEnd(pathEnd); + } + } + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + public static final int find(MapElement[] map, CharChunk name) { + return find(map, name, name.getStart(), name.getEnd()); + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + private static final int find(MapElement[] map, CharChunk name, + int start, int end) { + + int a = 0; + int b = map.length - 1; + + // Special cases: -1 and 0 + if (b == -1) { + return -1; + } + + if (compare(name, start, end, map[0].name) < 0 ) { + return -1; + } + if (b == 0) { + return 0; + } + + int i = 0; + while (true) { + i = (b + a) / 2; + int result = compare(name, start, end, map[i].name); + if (result == 1) { + a = i; + } else if (result == 0) { + return i; + } else { + b = i; + } + if ((b - a) == 1) { + int result2 = compare(name, start, end, map[b].name); + if (result2 < 0) { + return a; + } else { + return b; + } + } + } + + } + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + private static final int findIgnoreCase(MapElement[] map, CharChunk name) { + return findIgnoreCase(map, name, name.getStart(), name.getEnd()); + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + private static final int findIgnoreCase(MapElement[] map, CharChunk name, + int start, int end) { + + int a = 0; + int b = map.length - 1; + + // Special cases: -1 and 0 + if (b == -1) { + return -1; + } + if (compareIgnoreCase(name, start, end, map[0].name) < 0 ) { + return -1; + } + if (b == 0) { + return 0; + } + + int i = 0; + while (true) { + i = (b + a) / 2; + int result = compareIgnoreCase(name, start, end, map[i].name); + if (result == 1) { + a = i; + } else if (result == 0) { + return i; + } else { + b = i; + } + if ((b - a) == 1) { + int result2 = compareIgnoreCase(name, start, end, map[b].name); + if (result2 < 0) { + return a; + } else { + return b; + } + } + } + + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + public static final int find(MapElement[] map, String name) { + + int a = 0; + int b = map.length - 1; + + // Special cases: -1 and 0 + if (b == -1) { + return -1; + } + + if (name.compareTo(map[0].name) < 0) { + return -1; + } + if (b == 0) { + return 0; + } + + int i = 0; + while (true) { + i = (b + a) / 2; + int result = name.compareTo(map[i].name); + if (result > 0) { + a = i; + } else if (result == 0) { + return i; + } else { + b = i; + } + if ((b - a) == 1) { + int result2 = name.compareTo(map[b].name); + if (result2 < 0) { + return a; + } else { + return b; + } + } + } + + } + + + /** + * Compare given char chunk with String. + * Return -1, 0 or +1 if inferior, equal, or superior to the String. + */ + private static final int compare(CharChunk name, int start, int end, + String compareTo) { + int result = 0; + char[] c = name.getBuffer(); + int len = compareTo.length(); + if ((end - start) < len) { + len = end - start; + } + for (int i = 0; (i < len) && (result == 0); i++) { + if (c[i + start] > compareTo.charAt(i)) { + result = 1; + } else if (c[i + start] < compareTo.charAt(i)) { + result = -1; + } + } + if (result == 0) { + if (compareTo.length() > (end - start)) { + result = -1; + } else if (compareTo.length() < (end - start)) { + result = 1; + } + } + return result; + } + + + /** + * Compare given char chunk with String ignoring case. + * Return -1, 0 or +1 if inferior, equal, or superior to the String. + */ + private static final int compareIgnoreCase(CharChunk name, int start, int end, + String compareTo) { + int result = 0; + char[] c = name.getBuffer(); + int len = compareTo.length(); + if ((end - start) < len) { + len = end - start; + } + for (int i = 0; (i < len) && (result == 0); i++) { + if (Ascii.toLower(c[i + start]) > Ascii.toLower(compareTo.charAt(i))) { + result = 1; + } else if (Ascii.toLower(c[i + start]) < Ascii.toLower(compareTo.charAt(i))) { + result = -1; + } + } + if (result == 0) { + if (compareTo.length() > (end - start)) { + result = -1; + } else if (compareTo.length() < (end - start)) { + result = 1; + } + } + return result; + } + + + /** + * Find the position of the last slash in the given char chunk. + */ + public static final int lastSlash(CharChunk name) { + + char[] c = name.getBuffer(); + int end = name.getEnd(); + int start = name.getStart(); + int pos = end; + + while (pos > start) { + if (c[--pos] == '/') { + break; + } + } + + return (pos); + + } + + + /** + * Find the position of the nth slash, in the given char chunk. + */ + public static final int nthSlash(CharChunk name, int n) { + + char[] c = name.getBuffer(); + int end = name.getEnd(); + int start = name.getStart(); + int pos = start; + int count = 0; + + while (pos < end) { + if ((c[pos++] == '/') && ((++count) == n)) { + pos--; + break; + } + } + + return (pos); + + } + + + /** + * Return the slash count in a given string. + */ + public static final int slashCount(String name) { + int pos = -1; + int count = 0; + while ((pos = name.indexOf('/', pos + 1)) != -1) { + count++; + } + return count; + } + + + /** + * Insert into the right place in a sorted MapElement array, and prevent + * duplicates. + */ + public static final boolean insertMap + (MapElement[] oldMap, MapElement[] newMap, MapElement newElement) { + int pos = find(oldMap, newElement.name); + if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) { + return false; + } + System.arraycopy(oldMap, 0, newMap, 0, pos + 1); + newMap[pos + 1] = newElement; + System.arraycopy + (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1); + return true; + } + + + /** + * Insert into the right place in a sorted MapElement array. + */ + public static final boolean removeMap + (MapElement[] oldMap, MapElement[] newMap, String name) { + int pos = find(oldMap, name); + if ((pos != -1) && (name.equals(oldMap[pos].name))) { + System.arraycopy(oldMap, 0, newMap, 0, pos); + System.arraycopy(oldMap, pos + 1, newMap, pos, + oldMap.length - pos - 1); + return true; + } + return false; + } + + + // ------------------------------------------------- MapElement Inner Class + + + protected static abstract class MapElement { + /** hostname or path + */ + public String name = null; + public Object object = null; + + public String toString() { + return "MapElement: \"" + name +"\""; + } + } + + + // ---------------------------------------------------- Context Inner Class + + + public static final class ContextMapElement + extends MapElement { + + public String[] welcomeResources = new String[0]; + public File resources = null; + public WrapperMapElement defaultWrapper = null; + public WrapperMapElement[] exactWrappers = new WrapperMapElement[0]; + public WrapperMapElement[] wildcardWrappers = new WrapperMapElement[0]; + public WrapperMapElement[] extensionWrappers = new WrapperMapElement[0]; + public int nesting = 0; + + public String toString() { + return "ContextMapElement {" + + "name: \"" + name + + "\"\nnesting: \"" + nesting + + "\"\n}"; + } + } + + + // ---------------------------------------------------- Wrapper Inner Class + + + public static class WrapperMapElement + extends MapElement { + public boolean jspWildCard = false; + } + + + public void destroy() { + } + + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, + ServletException { + } + + + public void init(FilterConfig filterConfig) throws ServletException { + } + + +} diff --git a/modules/tomcat-lite/java/org/apache/coyote/servlet/config.properties b/modules/tomcat-lite/java/org/apache/coyote/servlet/config.properties new file mode 100644 index 000000000..0a5d65840 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/coyote/servlet/config.properties @@ -0,0 +1,34 @@ +# Support for PropertiesSpi - the 'dummy' framework used for tomcat-lite +# If tomcat is used with a proper framework, you need to bind and configure +# those objects in the framework. + +Main.(class)=org.apache.coyote.servlet.TomcatLite + +Jmx.(class)=org.apache.tomcat.integration.jmx.JmxObjectManagerSpi + +# --- Class names for required plugin interfaces --- + +org.apache.coyote.servlet.WebappServletMapper.(class)=org.apache.coyote.servlet.WebappServletMapper +org.apache.coyote.servlet.WebappFilterMapper.(class)=org.apache.coyote.servlet.WebappFilterMapper + +# Sessions +org.apache.tomcat.servlets.session.UserSessionManager.(class)=org.apache.tomcat.servlets.session.SimpleSessionManager + +# Loader for web.xml - you can have your own custom class using a more efficient +# or hardcoded. +org.apache.coyote.servlet.ContextPreinitListener.(class)=org.apache.coyote.servlet.webxml.TomcatLiteWebXmlConfig + +# Connector class +org.apache.coyote.servlet.Connector.(class)=org.apache.coyote.servlet.CoyoteConnector + +# JMX +jmx-connector.(class)=org.apache.tomcat.integration.jmx.JmxObjectManagerSpi + +# --- Other required settings --- + +# Customize default and *.jsp mappings +default-servlet.(class)=org.apache.tomcat.servlets.file.WebdavServlet +jspwildcard-servlet.(class)=org.apache.tomcat.servlets.jsp.WildcardTemplateServlet +filetemplate-servlet.(class)=org.apache.tomcat.servlets.jsp.JspFileTemplateServlet + + diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/BodyReader.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/BodyReader.java deleted file mode 100644 index e8f8a6be5..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/BodyReader.java +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.util.HashMap; - -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. - * - */ - -/** - * 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 BodyReader 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. More data may be added to it while reading. - */ - private ByteChunk bb; - - - /** - * The chunk buffer, will be filled in from the bb. - */ - 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; - - /** - * Encoding to use. - */ - private String enc; - - - /** - * Encoder is set. - */ - private boolean gotEnc = false; - - - /** - * Cached encoders. - */ - protected HashMap encoders = - new HashMap(); - - - /** - * Current byte to char converter. - */ - protected B2CConverter conv; - - - /** - * Associated Coyote request. - */ - private ServletRequestImpl coyoteRequest; - Connector connector; - - /** - * Buffer position. - */ - private int markPos = -1; - - - /** - * Buffer size. - */ - private int size = -1; - - - // ----------------------------------------------------------- Constructors - - - /** - * Default constructor. Allocate the buffer with the default buffer size. - */ - public BodyReader() { - this(DEFAULT_BUFFER_SIZE); - } - - - /** - * Alternate constructor which allows specifying the initial buffer size. - * - * @param size Buffer size to use - */ - public BodyReader(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 setConnector(Connector c, ServletRequestImpl coyoteRequest) { - this.connector = c; - this.coyoteRequest = 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 = connector.doRead(coyoteRequest, 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; - } - - /** - * Called when a read(char[]) operation is lacking data. It will read - * bytes. - */ - 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, -1); - 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(); - } - } - - - 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) { - conv = new B2CConverter(enc); - encoders.put(enc, conv); - } - } - - public class MRInputStream extends InputStream { - public long skip(long n) - throws IOException { - return BodyReader.this.skip(n); - } - - public void mark(int readAheadLimit) - { - try { - BodyReader.this.mark(readAheadLimit); - } catch (IOException e) { - e.printStackTrace(); - } - } - - - public void reset() - throws IOException { - BodyReader.this.reset(); - } - - - - public int read() - throws IOException { - return BodyReader.this.readByte(); - } - - public int available() throws IOException { - return BodyReader.this.available(); - } - - public int read(final byte[] b) throws IOException { - return BodyReader.this.read(b, 0, b.length); - } - - - public int read(final byte[] b, final int off, final int len) - throws IOException { - - return BodyReader.this.read(b, off, len); - } - - - /** - * Close the stream - * Since we re-cycle, we can't allow the call to super.close() - * which would permantely disable us. - */ - public void close() throws IOException { - BodyReader.this.close(); - } - } - - MRInputStream is = new MRInputStream(); - - public InputStream asInputStream() { - return is; - } -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/BodyWriter.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/BodyWriter.java deleted file mode 100644 index 5e09ae77f..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/BodyWriter.java +++ /dev/null @@ -1,671 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -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.tomcat.util.buf.ByteChunk; -import org.apache.tomcat.util.buf.C2BConverter; -import org.apache.tomcat.util.buf.CharChunk; - -/** - * Implement buffering and character translation acording to the - * servlet spec. - * - * This class handles both chars and bytes. - * - * It is tightly integrated with servlet response, sending headers - * and updating the commit state. - * - * TODO: add 'extension' interface that allows direct access to - * the async connector non-copy non-blocking queue. Same for the - * OutputStream. Maybe switch the buffer to the brigade. - * - * @author Costin Manolache - */ -public class BodyWriter extends Writer { - - // used in getWriter, until a method is added to res. - protected static final int WRITER_NOTE = 3; - - - private ByteChunk.ByteOutputChannel byteFlusher = - new ByteChunk.ByteOutputChannel() { - - @Override - public void realWriteBytes(byte[] cbuf, int off, int len) - throws IOException { - BodyWriter.this.realWriteBytes(cbuf, off, len); - } - }; - - private CharChunk.CharOutputChannel charFlusher = - new CharChunk.CharOutputChannel() { - @Override - public void realWriteChars(char[] cbuf, int off, int len) - throws IOException { - BodyWriter.this.realWriteChars(cbuf, off, len); - } - }; - - - 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 CHAR_STATE = 1; - public final int BYTE_STATE = 2; - - boolean headersSent = false; - // ----------------------------------------------------- Instance Variables - ServletResponseImpl res; - - /** - * The byte buffer. - */ - protected ByteChunk bb; - - - /** - * The chunk buffer. - */ - protected CharChunk cb; - - - /** - * State of the output buffer. - */ - protected int state = 0; - - - /** - * Number of bytes written. - */ - protected int bytesWritten = 0; - - - /** - * Number of chars written. - */ - protected int charsWritten = 0; - - - /** - * Flag which indicates if the output buffer is closed. - */ - protected boolean closed = false; - - - /** - * Do a flush on the next operation. - */ - protected boolean doFlush = false; - - - /** - * Byte chunk used to output bytes. This is just used to wrap the byte[] - * to match the coyote OutputBuffer interface - */ - protected ByteChunk outputChunk = new ByteChunk(); - - - /** - * Encoding to use. - * TODO: isn't it redundant ? enc, gotEnc, conv plus the enc in the bb - */ - protected String enc; - - - /** - * Encoder is set. - */ - protected boolean gotEnc = false; - - - /** - * List of encoders. The writer is reused - the encoder mapping - * avoids creating expensive objects. In future it'll contain nio.Charsets - */ - protected HashMap encoders = new HashMap(); - - - /** - * Current char to byte converter. TODO: replace with Charset - */ - protected C2BConverter conv; - - /** - * Suspended flag. All output bytes will be swallowed if this is true. - */ - protected boolean suspended = false; - - private Connector connector; - - - // ----------------------------------------------------------- Constructors - - - /** - * Default constructor. Allocate the buffer with the default buffer size. - */ - public BodyWriter() { - - this(DEFAULT_BUFFER_SIZE); - - } - - - /** - * Alternate constructor which allows specifying the initial buffer size. - * - * @param size Buffer size to use - */ - public BodyWriter(int size) { - - bb = new ByteChunk(size); - bb.setLimit(size); - bb.setByteOutputChannel(byteFlusher); - cb = new CharChunk(size); - cb.setCharOutputChannel(charFlusher); - cb.setLimit(size); - - } - - public void setConnector(Connector c, ServletResponseImpl res) { - this.res = res; - this.connector = c; - } - - - // ------------------------------------------------------------- Properties - - - /** - * 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 = BYTE_STATE; - headersSent = false; - 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 (state == CHAR_STATE) { - cb.flushBuffer(); - state = BYTE_STATE; - } - connector.beforeClose(res, bb.getLength()); - - doFlush(false); - closed = true; - - connector.finishResponse(res); - } - - - /** - * 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 (!headersSent) { - // If the buffers are empty, commit the response header - connector.sendHeaders(res); - headersSent = true; - } - if (state == CHAR_STATE) { - cb.flushBuffer(); - state = BYTE_STATE; - } - if (state == BYTE_STATE) { - bb.flushBuffer(); - } - doFlush = false; - - if (realFlush) { - connector.realFlush(res); - } - - } - - - // ------------------------------------------------- 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 - */ - private void realWriteBytes(byte buf[], int off, int cnt) - throws IOException { - - if (closed) - return; - - // If we really have something to write - if (cnt > 0) { - // real write to the adapter - outputChunk.setBytes(buf, off, cnt); - try { - connector.doWrite(res, 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(StringBuilder 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 println() throws IOException { - write("\n"); - } - - public void println(String s) throws IOException { - write(s); - write("\n"); - } - - public void print(String s) throws IOException { - write(s); - } - - public void flushChars() - throws IOException { - - cb.flushBuffer(); - state = BYTE_STATE; - - } - - - public boolean flushCharsNeeded() { - return state == CHAR_STATE; - } - - - public void setEncoding(String s) { - enc = s; - } - - - private 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 { - - enc = res.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 = BYTE_STATE; - } - - - public int getBufferSize() { - return bb.getLimit(); - } - - public ByteChunk getByteBuffer() { - return outputChunk; - } - -} -//{ -// public abstract int getBytesWritten(); -// public abstract int getCharsWritten(); -// public abstract void recycle(); -// public abstract void setSuspended(boolean suspended); -// public abstract boolean isSuspended(); -// -// public abstract void reset(); -// public abstract int getBufferSize(); -// public abstract void setBufferSize(int n); -// public abstract void checkConverter() throws IOException; -// public boolean isNew() { -// return getBytesWritten() == 0 && getCharsWritten() == 0; -// } -// public abstract void write(byte[] b, int off, int len) throws IOException; -// public abstract void writeByte(int b) throws IOException; -// -//} \ No newline at end of file diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/CharsetMapperDefault.properties b/modules/tomcat-lite/java/org/apache/tomcat/lite/CharsetMapperDefault.properties deleted file mode 100644 index c73a8fc4c..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/CharsetMapperDefault.properties +++ /dev/null @@ -1,2 +0,0 @@ -en=ISO-8859-1 -fr=ISO-8859-1 diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ClientAbortException.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ClientAbortException.java deleted file mode 100644 index ab395a3a7..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ClientAbortException.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -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() { - - StringBuilder sb = new StringBuilder("ClientAbortException: "); - if (message != null) { - sb.append(message); - if (throwable != null) { - sb.append(": "); - } - } - if (throwable != null) { - sb.append(throwable.toString()); - } - return (sb.toString()); - - } - - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/Connector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/Connector.java deleted file mode 100644 index b5a4fdf66..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/Connector.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.tomcat.integration.ObjectManager; -import org.apache.tomcat.util.buf.ByteChunk; - -/** - * What we need to plugin a connector. - * - * Currently we have lots of deps on coyote Request, but I plan to - * change this and allow other HTTP implementations - like MINA, the - * experimental async connector, etc. Most important will be - * different support for IO - i.e. a more non-blocking mode. - * We'll probably keep MessageBytes as wrappers for request/res - * properties. - * - * This interface has no dep on coyote. - * - */ -public interface Connector { - - public void setDaemon(boolean b); - - public void start() throws IOException; - - public void stop() throws Exception; - - /** - * Called during close() - either on explicit output close, or - * after the request is completed. - * - * @throws IOException - */ - public abstract void finishResponse(HttpServletResponse res) throws IOException; - - /** - * Called before flushing the output during close. - * Content-Length may be updated. - * @param len - * - * @throws IOException - */ - public abstract void beforeClose(HttpServletResponse res, int len) throws IOException; - - /** - * Called when the first flush() is called. - * @throws IOException - */ - public abstract void sendHeaders(HttpServletResponse res) throws IOException; - - /** - * Send data to the client. - * @throws IOException - */ - public abstract void realFlush(HttpServletResponse res) throws IOException; - - /** - * Write to the connector underlying buffer. - * The chunk will be reused (currently). - */ - public abstract void doWrite(HttpServletResponse res, ByteChunk outputChunk2) throws IOException; - - - //public void finishResponse(HttpServletResponse res) throws IOException; - - public void acknowledge(HttpServletResponse res) throws IOException; - - public void reset(HttpServletResponse res); - - public void recycle(HttpServletRequest req, HttpServletResponse res); - - void initRequest(HttpServletRequest req, HttpServletResponse res); - - public void setTomcatLite(TomcatLite tomcatLite); - - public void setObjectManager(ObjectManager objectManager); - - public String getRemoteHost(HttpServletRequest req); - - public String getRemoteAddr(HttpServletRequest req); - - public int doRead(ServletRequestImpl coyoteRequest, ByteChunk bb) throws IOException; -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ContextPreinitListener.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ContextPreinitListener.java deleted file mode 100644 index 663266e3b..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ContextPreinitListener.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -import javax.servlet.ServletContext; - -/** - * Tomcat-lite specific interface ( could be moved to addons ). - * This class will be called before initialization - implementations - * can add servlets, filters, etc. In particular web.xml parsing - * is done implementing this interface. - * - * On a small server you could remove web.xml support to reduce - * footprint, and either hardcode this class or use properties. - * Same if you already use a framework and you inject settings - * or use framework's registry (OSGI). - * - * @author Costin Manolache - */ -public interface ContextPreinitListener { - - public void preInit(ServletContext ctx); -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/FilterChainImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/FilterChainImpl.java deleted file mode 100644 index 7d3569b6b..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/FilterChainImpl.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2009 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.tomcat.lite; - - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.Servlet; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -/** - * Wraps the list of filters for the current request. One instance - * associated with each RequestImpl, reused. - * - * Populated by the mapper ( WebappFilterMapper for example ), which - * determines the filters for the current request. - * - * Not thread safe. - */ -public final class FilterChainImpl implements FilterChain { - private List filters = new ArrayList(); - - - /** - * The int which is used to maintain the current position - * in the filter chain. - */ - private int pos = 0; - - /** - * The servlet instance to be executed by this chain. - */ - private Servlet servlet = null; - - - private ServletConfigImpl wrapper; - - - public FilterChainImpl() { - super(); - } - - - /** - * Invoke the next filter in this chain, passing the specified request - * and response. If there are no more filters in this chain, invoke - * the service() method of the servlet itself. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet exception occurs - */ - public void doFilter(ServletRequest request, ServletResponse response) - throws IOException, ServletException { - - - // Call the next filter if there is one - if (pos < filters.size()) { - FilterConfigImpl filterConfig = filters.get(pos++); - Filter filter = null; - try { - filter = filterConfig.getFilter(); - filter.doFilter(request, response, this); - } catch (IOException e) { - throw e; - } catch (ServletException e) { - throw e; - } catch (RuntimeException e) { - throw e; - } catch (Throwable e) { - e.printStackTrace(); - throw new ServletException("Throwable", e); - } - return; - } - - // We fell off the end of the chain -- call the servlet instance - try { - if (servlet != null) - servlet.service(request, response); - } catch (IOException e) { - throw e; - } catch (ServletException e) { - throw e; - } catch (RuntimeException e) { - throw e; - } catch (Throwable e) { - throw new ServletException("Throwable", e); - } - } - - - // -------------------------------------------------------- Package Methods - - - - /** - * Add a filter to the set of filters that will be executed in this chain. - * - * @param filterConfig The FilterConfig for the servlet to be executed - */ - public void addFilter(FilterConfigImpl filterConfig) { - filters.add(filterConfig); - } - - - /** - * Release references to the filters and wrapper executed by this chain. - */ - public void release() { - filters.clear(); - pos = 0; - servlet = null; - } - - - /** - * Set the servlet that will be executed at the end of this chain. - * Set by the mapper filter - */ - public void setServlet(ServletConfigImpl wrapper, Servlet servlet) { - this.wrapper = wrapper; - this.servlet = servlet; - } - - // ------ Getters for information ------------ - - public int getSize() { - return filters.size(); - } - - public FilterConfigImpl getFilter(int i) { - return filters.get(i); - } - - public Servlet getServlet() { - return servlet; - } - - public ServletConfigImpl getServletConfig() { - return wrapper; - } - - public int getPos() { - return pos; - } -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/FilterConfigImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/FilterConfigImpl.java deleted file mode 100644 index b3b595140..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/FilterConfigImpl.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * 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.tomcat.lite; - - -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.servlet.DispatcherType; -import javax.servlet.Filter; -import javax.servlet.FilterConfig; -import javax.servlet.FilterRegistration; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.FilterRegistration.Dynamic; - -import org.apache.tomcat.servlets.util.Enumerator; - - -/** - * A Filter is configured in web.xml by: - * - name - used in mappings - * - className - used to instantiate the filter - * - init params - * - other things not used in the servlet container ( icon, descr, etc ) - * - * Alternatively, in API mode you can pass the actual filter. - * - * @see ServletConfigImpl - */ -public final class FilterConfigImpl implements FilterConfig, FilterRegistration { - - DynamicFilterRegistration dynamic = new DynamicFilterRegistration(); - - public FilterConfigImpl(ServletContextImpl context) { - this.ctx = context; - } - - boolean asyncSupported; - - private ServletContextImpl ctx = null; - - /** - * The application Filter we are configured for. - */ - private transient Filter filter = null; - - String descryption; - - private String filterName; - - private String filterClassName; - - Map initParams; - - private Class filterClass; - - public void setData(String filterName, String filterClass, - Map params) { - this.filterName = filterName; - this.filterClassName = filterClass; - this.initParams = params; - } - - public void setFilter(Filter f) { - filter = f; - } - - public String getFilterName() { - return filterName; - } - - public void setFilterClass(Class filterClass2) { - this.filterClass = filterClass2; - } - - - public String getInitParameter(String name) { - if (initParams == null) return null; - return initParams.get(name); - } - - /** - * Return an Enumeration of the names of the initialization - * parameters for this Filter. - */ - public Enumeration getInitParameterNames() { - if (initParams == null) - return (new Enumerator(new ArrayList())); - else - return (new Enumerator(initParams.keySet())); - } - - - /** - * Return the ServletContext of our associated web application. - */ - public ServletContext getServletContext() { - return ctx; - } - - /** - * Return the application Filter we are configured for. - */ - public Filter createFilter() throws ClassCastException, ClassNotFoundException, - IllegalAccessException, InstantiationException, ServletException { - - // Return the existing filter instance, if any - if (filter != null) - return filter; - - ClassLoader classLoader = ctx.getClassLoader(); - - ClassLoader oldCtxClassLoader = - Thread.currentThread().getContextClassLoader(); - if (classLoader != oldCtxClassLoader) { - Thread.currentThread().setContextClassLoader(classLoader); - } - try { - if (filterClass == null) { - filterClass = (Class) classLoader.loadClass(filterClassName); - } - this.filter = (Filter) filterClass.newInstance(); - } finally { - if (classLoader != oldCtxClassLoader) { - Thread.currentThread().setContextClassLoader(oldCtxClassLoader); - } - } - - // TODO: resource injection - - return filter; - } - - public Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException { - Filter filter = createFilter(); - filter.init(this); - return (this.filter); - } - - - /** - * Release the Filter instance associated with this FilterConfig, - * if there is one. - */ - public void release() { - if (this.filter != null){ - filter.destroy(); - } - this.filter = null; - } - - - @Override - public void addMappingForServletNames(EnumSet dispatcherTypes, - boolean isMatchAfter, - String... servletNames) { - if (ctx.startDone) { - // Use the context method instead of the servlet API to - // add mappings after context init. - throw new IllegalStateException(); - } - ArrayList dispatchers = new ArrayList(); - for (DispatcherType dt: dispatcherTypes) { - dispatchers.add(dt.name()); - } - for (String servletName: servletNames) { - ctx.getFilterMapper().addMapping(getFilterName(), - null, servletName, (String[]) dispatchers.toArray(), isMatchAfter); - } - } - - - @Override - public void addMappingForUrlPatterns(EnumSet dispatcherTypes, - boolean isMatchAfter, - String... urlPatterns) { - if (ctx.startDone) { - // Use the context method instead of the servlet API to - // add mappings after context init. - throw new IllegalStateException(); - } - ArrayList dispatchers = new ArrayList(); - for (DispatcherType dt: dispatcherTypes) { - dispatchers.add(dt.name()); - } - for (String url: urlPatterns) { - ctx.getFilterMapper().addMapping(getFilterName(), - url, null, (String[]) dispatchers.toArray(), isMatchAfter); - } - } - - - @Override - public boolean setInitParameter(String name, String value) - throws IllegalArgumentException, IllegalStateException { - return ServletContextImpl.setInitParameter(ctx, initParams, - name, value); - } - - - @Override - public Set setInitParameters(Map initParameters) - throws IllegalArgumentException, IllegalStateException { - return ServletContextImpl.setInitParameters(ctx, initParams, - initParameters); - } - - public Dynamic getDynamic() { - return dynamic; - } - - public class DynamicFilterRegistration implements Dynamic { - - - @Override - public void addMappingForServletNames(EnumSet dispatcherTypes, - boolean isMatchAfter, - String... servletNames) { - FilterConfigImpl.this.addMappingForServletNames(dispatcherTypes, isMatchAfter, servletNames); - } - - - @Override - public void addMappingForUrlPatterns(EnumSet dispatcherTypes, - boolean isMatchAfter, - String... urlPatterns) { - FilterConfigImpl.this.addMappingForUrlPatterns(dispatcherTypes, isMatchAfter, urlPatterns); - } - - - @Override - public boolean setInitParameter(String name, String value) - throws IllegalArgumentException, IllegalStateException { - return ServletContextImpl.setInitParameter(ctx, initParams, - name, value); - } - - - @Override - public Set setInitParameters(Map initParameters) - throws IllegalArgumentException, IllegalStateException { - return ServletContextImpl.setInitParameters(ctx, initParams, - initParameters); - } - - - @Override - public void setAsyncSupported(boolean isAsyncSupported) - throws IllegalStateException { - asyncSupported = isAsyncSupported; - } - - - public void setDescription(String description) - throws IllegalStateException { - FilterConfigImpl.this.descryption = description; - } - - @Override - public Collection getUrlPatternMappings() { - // implement me - return null; - } - - @Override - public Collection getServletNameMappings() { - // implement me - return null; - } - - @Override - public Map getInitParameters() { - // implement me - return null; - } - - @Override - public String getInitParameter(String name) { - if (initParams == null) return null; - return initParams.get(name); - } - - @Override - public String getClassName() { - // implement me - return null; - } - - @Override - public String getName() { - // implement me - return null; - } - } - - @Override - public Collection getUrlPatternMappings() { - // implement me - return null; - } - - @Override - public Collection getServletNameMappings() { - // implement me - return null; - } - - @Override - public Map getInitParameters() { - // implement me - return null; - } - - @Override - public String getClassName() { - // implement me - return null; - } - - @Override - public String getName() { - // implement me - return null; - } - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/Locale2Charset.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/Locale2Charset.java deleted file mode 100644 index 0a9d73a6a..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/Locale2Charset.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.tomcat.lite; - - -import java.io.InputStream; -import java.util.Locale; -import java.util.Properties; - - - -/** - * One instance per Context. Holds the - * - * Utility class that attempts to map from a Locale to the corresponding - * character set to be used for interpreting input text (or generating - * output text) when the Content-Type header does not include one. You - * can customize the behavior of this class by modifying the mapping data - * it loads, or by subclassing it (to change the algorithm) and then using - * your own version for a particular web application. - * - * @author Craig R. McClanahan - */ -public class Locale2Charset { - - - // ---------------------------------------------------- Manifest Constants - - - /** - * Default properties resource name. - */ - public static final String DEFAULT_RESOURCE = - "/org/apache/tomcat/lite/CharsetMapperDefault.properties"; - - - - // ---------------------------------------------------------- Constructors - - - /** - * Construct a new CharsetMapper using the default properties resource. - */ - public Locale2Charset() { - String name = DEFAULT_RESOURCE; - if (defaultMap == null) { // once ! - try { - defaultMap = new Properties(); - InputStream stream = - this.getClass().getResourceAsStream(name); - defaultMap.load(stream); - stream.close(); - } catch (Throwable t) { - throw new IllegalArgumentException(t.toString()); - } - } - map = defaultMap; - } - - - // ---------------------------------------------------- Instance Variables - - - private static Properties defaultMap; // shared for all apps - - /** - * The mapping properties that have been initialized from the specified or - * default properties resource. - */ - private Properties map; - - - // ------------------------------------------------------- Public Methods - - - /** - * Calculate the name of a character set to be assumed, given the specified - * Locale and the absence of a character set specified as part of the - * content type header. - * - * @param locale The locale for which to calculate a character set - */ - public String getCharset(Locale locale) { - // Match full language_country_variant first, then language_country, - // then language only - String charset = map.getProperty(locale.toString()); - if (charset == null) { - charset = map.getProperty(locale.getLanguage() + "_" - + locale.getCountry()); - if (charset == null) { - charset = map.getProperty(locale.getLanguage()); - } - } - return (charset); - } - - - /** - * The deployment descriptor can have a - * locale-encoding-mapping-list element which describes the - * webapp's desired mapping from locale to charset. This method - * gets called when processing the web.xml file for a context - * - * @param locale The locale for a character set - * @param charset The charset to be associated with the locale - */ - public void addCharsetMapping(String locale, String charset) { - if (map == defaultMap) { - // new copy, don't modify original - map = new Properties(defaultMap); - } - map.put(locale, charset); - } -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ParameterMap.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ParameterMap.java deleted file mode 100644 index 045ecfb00..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ParameterMap.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * 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.tomcat.lite; - - -import java.util.HashMap; -import java.util.Map; - - -/** - * Extended implementation of HashMap that includes a - * locked property. This class can be used to safely expose - * Catalina internal parameter map objects to user classes without having - * to clone them in order to avoid modifications. When first created, a - * ParmaeterMap instance is not locked. - * - * @author Craig R. McClanahan - * @version $Revision$ $Date$ - */ - -public final class ParameterMap extends HashMap { - - - // ----------------------------------------------------------- Constructors - - - /** - * Construct a new, empty map with the default initial capacity and - * load factor. - */ - public ParameterMap() { - - super(); - - } - - - /** - * Construct a new, empty map with the specified initial capacity and - * default load factor. - * - * @param initialCapacity The initial capacity of this map - */ - public ParameterMap(int initialCapacity) { - - super(initialCapacity); - - } - - - /** - * Construct a new, empty map with the specified initial capacity and - * load factor. - * - * @param initialCapacity The initial capacity of this map - * @param loadFactor The load factor of this map - */ - public ParameterMap(int initialCapacity, float loadFactor) { - - super(initialCapacity, loadFactor); - - } - - - /** - * Construct a new map with the same mappings as the given map. - * - * @param map Map whose contents are dupliated in the new map - */ - public ParameterMap(Map map) { - - super(map); - - } - - - // ------------------------------------------------------------- Properties - - - /** - * The current lock state of this parameter map. - */ - private boolean locked = false; - - - /** - * Return the locked state of this parameter map. - */ - public boolean isLocked() { - - return (this.locked); - - } - - - /** - * Set the locked state of this parameter map. - * - * @param locked The new locked state - */ - public void setLocked(boolean locked) { - - this.locked = locked; - - } - - - // --------------------------------------------------------- Public Methods - - - - /** - * Remove all mappings from this map. - * - * @exception IllegalStateException if this map is currently locked - */ - public void clear() { - - if (locked) - throw new IllegalStateException - ("parameterMap.locked"); - super.clear(); - - } - - - /** - * Associate the specified value with the specified key in this map. If - * the map previously contained a mapping for this key, the old value is - * replaced. - * - * @param key Key with which the specified value is to be associated - * @param value Value to be associated with the specified key - * - * @return The previous value associated with the specified key, or - * null if there was no mapping for key - * - * @exception IllegalStateException if this map is currently locked - */ - public Object put(Object key, Object value) { - - if (locked) - throw new IllegalStateException - ("parameterMap.locked"); - return (super.put(key, value)); - - } - - - /** - * Copy all of the mappings from the specified map to this one. These - * mappings replace any mappings that this map had for any of the keys - * currently in the specified Map. - * - * @param map Mappings to be stored into this map - * - * @exception IllegalStateException if this map is currently locked - */ - public void putAll(Map map) { - - if (locked) - throw new IllegalStateException - ("parameterMap.locked"); - super.putAll(map); - - } - - - /** - * Remove the mapping for this key from the map if present. - * - * @param key Key whose mapping is to be removed from the map - * - * @return The previous value associated with the specified key, or - * null if there was no mapping for that key - * - * @exception IllegalStateException if this map is currently locked - */ - public Object remove(Object key) { - - if (locked) - throw new IllegalStateException - ("parameterMap.locked"); - return (super.remove(key)); - - } - - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/RequestDispatcherImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/RequestDispatcherImpl.java deleted file mode 100644 index 6ec239739..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/RequestDispatcherImpl.java +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletRequestWrapper; -import javax.servlet.ServletResponse; -import javax.servlet.ServletResponseWrapper; -import javax.servlet.UnavailableException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.tomcat.util.buf.CharChunk; -import org.apache.tomcat.util.buf.MessageBytes; -import org.apache.tomcat.util.http.mapper.MappingData; - -/** - * - */ -public final class RequestDispatcherImpl implements RequestDispatcher { - /** - * The request attribute under which the original servlet path is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_SERVLET_PATH_ATTR = - "javax.servlet.forward.servlet_path"; - - - /** - * The request attribute under which the original query string is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_QUERY_STRING_ATTR = - "javax.servlet.forward.query_string"; - - /** - * The request attribute under which the original request URI is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_REQUEST_URI_ATTR = - "javax.servlet.forward.request_uri"; - - - /** - * The request attribute under which the original context path is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_CONTEXT_PATH_ATTR = - "javax.servlet.forward.context_path"; - - - /** - * The request attribute under which the original path info is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_PATH_INFO_ATTR = - "javax.servlet.forward.path_info"; - - /** - * The request attribute under which we store the servlet name on a - * named dispatcher request. - */ - public static final String NAMED_DISPATCHER_ATTR = - "org.apache.catalina.NAMED"; - - /** - * The request attribute under which the request URI of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_REQUEST_URI_ATTR = - "javax.servlet.include.request_uri"; - - - /** - * The request attribute under which the context path of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_CONTEXT_PATH_ATTR = - "javax.servlet.include.context_path"; - - - /** - * The request attribute under which the path info of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_PATH_INFO_ATTR = - "javax.servlet.include.path_info"; - - - /** - * The request attribute under which the servlet path of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_SERVLET_PATH_ATTR = - "javax.servlet.include.servlet_path"; - - - /** - * The request attribute under which the query string of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_QUERY_STRING_ATTR = - "javax.servlet.include.query_string"; - - /** - * The request attribute under which we expose the value of the - * <jsp-file> value associated with this servlet, - * if any. - */ - public static final String JSP_FILE_ATTR = - "org.apache.catalina.jsp_file"; - - - // ----------------------------------------------------- Instance Variables - - private static Logger log = Logger.getLogger(RequestDispatcherImpl.class.getName()); - - private ServletContextImpl ctx = null; - - /** - * The servlet name for a named dispatcher. - */ - private String name = null; - - // Path for a path dispatcher - private String path; - - /** - * MappingData object - per thread for buffering. - */ - private transient ThreadLocal localMappingData = new ThreadLocal(); - - /* - OrigRequest(ServletRequestImpl) -> include/forward * -> this include - - On the path: user-defined RequestWrapper or our ServletRequestWrapper - - include() is called with a RequestWrapper(->...->origRequest) or origRequest - - Based on params, etc -> we wrap the req / response in ServletRequestWrapper, - call filters+servlet. Inside, the req can be wrapped again in - userReqWrapper, and other include called. - - - */ - - /** - * The outermost request that will be passed on to the invoked servlet. - */ - private ServletRequest outerRequest = null; - - /** - * The outermost response that will be passed on to the invoked servlet. - */ - private ServletResponse outerResponse = null; - - /** - * The request wrapper we have created and installed (if any). - */ - private ServletRequest wrapRequest = null; - - /** - * The response wrapper we have created and installed (if any). - */ - private ServletResponse wrapResponse = null; - - // Parameters used when constructing the dispatcvher - /** - * The extra path information for this RequestDispatcher. - */ - private String pathInfo = null; - /** - * The query string parameters for this RequestDispatcher. - */ - private String queryString = null; - /** - * The request URI for this RequestDispatcher. - */ - private String requestURI = null; - /** - * The servlet path for this RequestDispatcher. - */ - private String servletPath = null; - - // - private String origServletPath = null; - - /** - * The Wrapper associated with the resource that will be forwarded to - * or included. - */ - private ServletConfigImpl wrapper = null; - - private Servlet servlet; - - /** Named dispatcher - */ - public RequestDispatcherImpl(ServletConfigImpl wrapper, String name) { - this.wrapper = wrapper; - this.name = name; - this.ctx = (ServletContextImpl) wrapper.getServletContext(); - - } - - public RequestDispatcherImpl(ServletContextImpl ctx, String path) { - this.path = path; - this.ctx = ctx; - } - - - - /** - * Forward this request and response to another resource for processing. - * Any runtime exception, IOException, or ServletException thrown by the - * called servlet will be propogated to the caller. - * - * @param request The servlet request to be forwarded - * @param response The servlet response to be forwarded - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet exception occurs - */ - public void forward(ServletRequest request, ServletResponse response) - throws ServletException, IOException - { - // Reset any output that has been buffered, but keep headers/cookies - if (response.isCommitted()) { - throw new IllegalStateException("forward(): response.isComitted()"); - } - - try { - response.resetBuffer(); - } catch (IllegalStateException e) { - throw e; - } - - // Set up to handle the specified request and response - setup(request, response, false); - - // Identify the HTTP-specific request and response objects (if any) - HttpServletRequest hrequest = (HttpServletRequest) request; - - ServletRequestWrapperImpl wrequest = - (ServletRequestWrapperImpl) wrapRequest(); - - - if (name != null) { - wrequest.setRequestURI(hrequest.getRequestURI()); - wrequest.setContextPath(hrequest.getContextPath()); - wrequest.setServletPath(hrequest.getServletPath()); - wrequest.setPathInfo(hrequest.getPathInfo()); - wrequest.setQueryString(hrequest.getQueryString()); - - - } else { // path based - mapPath(); - if (wrapper == null) { - throw new ServletException("Forward not found " + - path); - } - String contextPath = ctx.getContextPath(); - if (hrequest.getAttribute(FORWARD_REQUEST_URI_ATTR) == null) { - wrequest.setAttribute(FORWARD_REQUEST_URI_ATTR, - hrequest.getRequestURI()); - wrequest.setAttribute(FORWARD_CONTEXT_PATH_ATTR, - hrequest.getContextPath()); - wrequest.setAttribute(FORWARD_SERVLET_PATH_ATTR, - hrequest.getServletPath()); - wrequest.setAttribute(FORWARD_PATH_INFO_ATTR, - hrequest.getPathInfo()); - wrequest.setAttribute(FORWARD_QUERY_STRING_ATTR, - hrequest.getQueryString()); - } - - wrequest.setContextPath(contextPath); - wrequest.setRequestURI(requestURI); - wrequest.setServletPath(servletPath); - wrequest.setPathInfo(pathInfo); - if (queryString != null) { - wrequest.setQueryString(queryString); - wrequest.setQueryParams(queryString); - } - } - processRequest(outerRequest, outerResponse); - - wrequest.recycle(); - unwrapRequest(); - - // This is not a real close in order to support error processing -// if ( log.isDebugEnabled() ) -// log.debug(" Disabling the response for futher output"); - - if (response instanceof ServletResponseImpl) { - ((ServletResponseImpl) response).flushBuffer(); - ((ServletResponseImpl) response).setSuspended(true); - } else { - // Servlet SRV.6.2.2. The Resquest/Response may have been wrapped - // and may no longer be instance of RequestFacade - if (log.isLoggable(Level.FINE)){ - log.fine( " The Response is vehiculed using a wrapper: " - + response.getClass().getName() ); - } - - // Close anyway - try { - PrintWriter writer = response.getWriter(); - writer.close(); - } catch (IllegalStateException e) { - try { - ServletOutputStream stream = response.getOutputStream(); - stream.close(); - } catch (IllegalStateException f) { - ; - } catch (IOException f) { - ; - } - } catch (IOException e) { - ; - } - } - } - - - - /** - * Include the response from another resource in the current response. - * Any runtime exception, IOException, or ServletException thrown by the - * called servlet will be propogated to the caller. - * - * @param request The servlet request that is including this one - * @param response The servlet response to be appended to - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet exception occurs - */ - public void include(ServletRequest request, ServletResponse response) - throws ServletException, IOException - { - - // Set up to handle the specified request and response - setup(request, response, true); - - // Create a wrapped response to use for this request - // this actually gets inserted somewhere in the chain - it's not - // the last one, but first non-user response - wrapResponse(); - ServletRequestWrapperImpl wrequest = - (ServletRequestWrapperImpl) wrapRequest(); - - - // Handle an HTTP named dispatcher include - if (name != null) { - wrequest.setAttribute(NAMED_DISPATCHER_ATTR, name); - if (servletPath != null) wrequest.setServletPath(servletPath); - wrequest.setAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR, - new Integer(WebappFilterMapper.INCLUDE)); - wrequest.setAttribute(WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR, - origServletPath); - } else { - mapPath(); - String contextPath = ctx.getContextPath(); - if (requestURI != null) - wrequest.setAttribute(INCLUDE_REQUEST_URI_ATTR, - requestURI); - if (contextPath != null) - wrequest.setAttribute(INCLUDE_CONTEXT_PATH_ATTR, - contextPath); - if (servletPath != null) - wrequest.setAttribute(INCLUDE_SERVLET_PATH_ATTR, - servletPath); - if (pathInfo != null) - wrequest.setAttribute(INCLUDE_PATH_INFO_ATTR, - pathInfo); - if (queryString != null) { - wrequest.setAttribute(INCLUDE_QUERY_STRING_ATTR, - queryString); - wrequest.setQueryParams(queryString); - } - - wrequest.setAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR, - new Integer(WebappFilterMapper.INCLUDE)); - wrequest.setAttribute(WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR, - origServletPath); - } - - invoke(outerRequest, outerResponse); - - wrequest.recycle(); - unwrapRequest(); - unwrapResponse(); - } - - - // -------------------------------------------------------- Private Methods - - public void mapPath() { - if (path == null || servletPath != null) return; - - // Retrieve the thread local URI, used for mapping - // TODO: recycle RequestDispatcher stack and associated objects - // instead of this object - - // Retrieve the thread local mapping data - MappingData mappingData = (MappingData) localMappingData.get(); - if (mappingData == null) { - mappingData = new MappingData(); - localMappingData.set(mappingData); - } - - // Get query string - int pos = path.indexOf('?'); - if (pos >= 0) { - queryString = path.substring(pos + 1); - } else { - pos = path.length(); - } - - // Map the URI - MessageBytes uriMB = MessageBytes.newInstance(); - CharChunk charBuffer = new CharChunk(); - //mappingData.localURIBytes; - uriMB.recycle(); - //CharChunk uriCC = uriMB.getCharChunk(); - try { - /* - * Ignore any trailing path params (separated by ';') for mapping - * purposes. - * This is sometimes broken - path params can be on any path - * component, not just last. - */ - int semicolon = path.indexOf(';'); - if (pos >= 0 && semicolon > pos) { - semicolon = -1; - } - if (ctx.getContextPath().length() > 1 ) { - charBuffer.append(ctx.getContextPath()); - } - charBuffer.append(path, 0, - semicolon > 0 ? semicolon : pos); - - // Wrap the buffer - uriMB.setChars(charBuffer.getBuffer(), - charBuffer.getOffset(), - charBuffer.getLength()); - - // TODO: make charBuffer part of request or something - ctx.getMapper().map(uriMB, mappingData); - - // at least default wrapper must be returned - - /* - * Append any trailing path params (separated by ';') that were - * ignored for mapping purposes, so that they're reflected in the - * RequestDispatcher's requestURI - */ - if (semicolon > 0) { - // I don't think this will be used in future - charBuffer.append(path, - semicolon, pos - semicolon); - } - } catch (Exception e) { - log.log(Level.SEVERE, "getRequestDispatcher()", e); - } - - wrapper = (ServletConfigImpl) mappingData.wrapper; - servletPath = mappingData.wrapperPath.toString(); - pathInfo = mappingData.pathInfo.toString(); - - mappingData.recycle(); - - } - - - /** - * Prepare the request based on the filter configuration. - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet error occurs - */ - private void processRequest(ServletRequest request, - ServletResponse response) - throws IOException, ServletException { - Integer disInt = - (Integer) request.getAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR); - if (disInt != null) { - if (disInt.intValue() != WebappFilterMapper.ERROR) { - outerRequest.setAttribute - (WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR, - origServletPath); - outerRequest.setAttribute - (WebappFilterMapper.DISPATCHER_TYPE_ATTR, - new Integer(WebappFilterMapper.FORWARD)); - } - invoke(outerRequest, response); - } - - } - - - - - /** - * Ask the resource represented by this RequestDispatcher to process - * the associated request, and create (or append to) the associated - * response. - *

- * IMPLEMENTATION NOTE: This implementation assumes - * that no filters are applied to a forwarded or included resource, - * because they were already done for the original request. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet error occurs - */ - private void invoke(ServletRequest request, ServletResponse response) - throws IOException, ServletException { - - // Checking to see if the context classloader is the current context - // classloader. If it's not, we're saving it, and setting the context - // classloader to the Context classloader - ClassLoader oldCCL = Thread.currentThread().getContextClassLoader(); - ClassLoader contextClassLoader = ctx.getClassLoader(); - - if (oldCCL != contextClassLoader) { - Thread.currentThread().setContextClassLoader(contextClassLoader); - } else { - oldCCL = null; - } - - // Initialize local variables we may need - HttpServletResponse hresponse = (HttpServletResponse) response; - IOException ioException = null; - ServletException servletException = null; - RuntimeException runtimeException = null; - - servletException = allocateServlet(hresponse, servletException); - - // Get the FilterChain Here - WebappFilterMapper factory = - ((ServletContextImpl)wrapper.getServletContext()).getFilterMapper(); - - FilterChainImpl filterChain = factory.createFilterChain(request, - wrapper, - servlet); - - // Call the service() method for the allocated servlet instance - try { - String jspFile = wrapper.getJspFile(); - if (jspFile != null) - request.setAttribute(JSP_FILE_ATTR, jspFile); - else - request.removeAttribute(JSP_FILE_ATTR); - // for includes/forwards - if ((servlet != null) && (filterChain != null)) { - filterChain.doFilter(request, response); - } - } catch (IOException e) { - ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " + - wrapper.getServletName(), e); - ioException = e; - } catch (UnavailableException e) { - ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " + - wrapper.getServletName(), e); - servletException = e; - wrapper.unavailable(e); - } catch (ServletException e) { - servletException = e; - } catch (RuntimeException e) { - ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " + - wrapper.getServletName(), e); - runtimeException = e; - } - request.removeAttribute(JSP_FILE_ATTR); - - // Release the filter chain (if any) for this request - if (filterChain != null) - filterChain.release(); - - servletException = servletDealocate(servletException); - - // Reset the old context class loader - if (oldCCL != null) - Thread.currentThread().setContextClassLoader(oldCCL); - - // Unwrap request/response if needed - unwrapRequest(); - unwrapResponse(); - - // Rethrow an exception if one was thrown by the invoked servlet - if (ioException != null) - throw ioException; - if (servletException != null) - throw servletException; - if (runtimeException != null) - throw runtimeException; - - } - - private ServletException servletDealocate(ServletException servletException) - { - if (servlet != null) { - wrapper.deallocate(servlet); - } - return servletException; - } - - private ServletException allocateServlet(HttpServletResponse hresponse, - ServletException servletException) - throws IOException - { - boolean unavailable = false; - - // Check for the servlet being marked unavailable - if (wrapper.isUnavailable()) { - ctx.getLogger().log(Level.WARNING, "isUnavailable() " + wrapper.getServletName()); - long available = wrapper.getAvailable(); - if ((available > 0L) && (available < Long.MAX_VALUE)) - hresponse.setDateHeader("Retry-After", available); - hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, - "Unavailable"); // No need to include internal info: wrapper.getServletName(); - unavailable = true; - } - - // Allocate a servlet instance to process this request - try { - if (!unavailable) { - servlet = wrapper.allocate(); - } - } catch (ServletException e) { - ctx.getLogger().log(Level.WARNING, "RequestDispatcher: allocate " + - wrapper.toString()); - servletException = e; - servlet = null; - } catch (Throwable e) { - ctx.getLogger().log(Level.WARNING, "allocate() error " + wrapper.getServletName(), e); - servletException = new ServletException - ("Allocate error " + wrapper.getServletName(), e); - servlet = null; - } - return servletException; - } - - - /** - * Set up to handle the specified request and response - * - * @param request The servlet request specified by the caller - * @param response The servlet response specified by the caller - * @param including Are we performing an include() as opposed to - * a forward()? - */ - private void setup(ServletRequest request, ServletResponse response, - boolean including) { - - this.outerRequest = request; - this.outerResponse = response; - } - - - /** - * Unwrap the request if we have wrapped it. Not sure how it could end - * up in the middle. - */ - private void unwrapRequest() { - if (wrapRequest == null) - return; - - ServletRequest previous = null; - ServletRequest current = outerRequest; - while (current != null) { - // If we run into the container request we are done - if (current instanceof ServletRequestImpl) - break; - - // Remove the current request if it is our wrapper - if (current == wrapRequest) { - ServletRequest next = - ((ServletRequestWrapper) current).getRequest(); - if (previous == null) - outerRequest = next; - else - ((ServletRequestWrapper) previous).setRequest(next); - break; - } - - // Advance to the next request in the chain - previous = current; - current = ((ServletRequestWrapper) current).getRequest(); - } - } - - - /** - * Unwrap the response if we have wrapped it. - */ - private void unwrapResponse() { - if (wrapResponse == null) - return; - - ServletResponse previous = null; - ServletResponse current = outerResponse; - while (current != null) { - // If we run into the container response we are done - if (current instanceof ServletResponseImpl) - break; - - // Remove the current response if it is our wrapper - if (current == wrapResponse) { - ServletResponse next = - ((ServletResponseWrapper) current).getResponse(); - if (previous == null) - outerResponse = next; - else - ((ServletResponseWrapper) previous).setResponse(next); - break; - } - // Advance to the next response in the chain - previous = current; - current = ((ServletResponseWrapper) current).getResponse(); - } - } - - - /** - * Create and return a request wrapper that has been inserted in the - * appropriate spot in the request chain. - */ - private ServletRequest wrapRequest() { - // Locate the request we should insert in front of - ServletRequest previous = null; - ServletRequest current = outerRequest; - while (current != null) { - if (!(current instanceof ServletRequestWrapper)) - break; - if (current instanceof ServletRequestWrapperImpl) - break; - if (current instanceof ServletRequestImpl) - break; - // user-specified - previous = current; - current = ((ServletRequestWrapper) current).getRequest(); - } - // now previous will be a user-specified wrapper, - // and current one of our own wrappers ( deeper in stack ) - // ... current USER_previous USER USER - // previous is null if the top request is ours. - - // Instantiate a new wrapper at this point and insert it in the chain - ServletRequest wrapper = null; - - // Compute a crossContext flag - boolean crossContext = isCrossContext(); - wrapper = - new ServletRequestWrapperImpl((HttpServletRequest) current, - ctx, crossContext); - - if (previous == null) { - // outer becomes the wrapper, includes orig wrapper inside - outerRequest = wrapper; - } else { - // outer remains user-specified sersvlet, delegating to - // our wrapper, which delegates to real request or our wrapper. - ((ServletRequestWrapper) previous).setRequest(wrapper); - } - wrapRequest = wrapper; - return (wrapper); - } - - private boolean isCrossContext() { - boolean crossContext = false; - if ((outerRequest instanceof ServletRequestWrapperImpl) || - (outerRequest instanceof ServletRequestImpl) || - (outerRequest instanceof HttpServletRequest)) { - HttpServletRequest houterRequest = - (HttpServletRequest) outerRequest; - Object contextPath = - houterRequest.getAttribute(INCLUDE_CONTEXT_PATH_ATTR); - if (contextPath == null) { - // Forward - contextPath = houterRequest.getContextPath(); - } - crossContext = !(ctx.getContextPath().equals(contextPath)); - } - return crossContext; - } - - - /** - * Create and return a response wrapper that has been inserted in the - * appropriate spot in the response chain. - * - * Side effect: updates outerResponse, wrapResponse. - * The chain is updated with a wrapper below lowest user wrapper - */ - private ServletResponse wrapResponse() { - // Locate the response we should insert in front of - ServletResponse previous = null; - ServletResponse current = outerResponse; - while (current != null) { - if (!(current instanceof ServletResponseWrapper)) - break; - if (current instanceof ServletResponseImpl) - break; - previous = current; - current = ((ServletResponseWrapper) current).getResponse(); - } - - // Instantiate a new wrapper at this point and insert it in the chain - ServletResponse wrapper = - new ServletResponseIncludeWrapper(current); - - if (previous == null) { - // outer is ours, we can wrap on top - outerResponse = wrapper; - } else { - // outer is user-specified, leave it alone. - // we insert ourself below the lowest user-specified response - ((ServletResponseWrapper) previous).setResponse(wrapper); - } - wrapResponse = wrapper; - return (wrapper); - - } - - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletConfigImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletConfigImpl.java deleted file mode 100644 index dee9826d1..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletConfigImpl.java +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Copyright 1999-2002,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.tomcat.lite; - -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; - - -import javax.servlet.MultipartConfigElement; -import javax.servlet.Servlet; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; -import javax.servlet.ServletSecurityElement; -import javax.servlet.SingleThreadModel; -import javax.servlet.UnavailableException; - -import org.apache.tomcat.addons.UserTemplateClassMapper; -import org.apache.tomcat.servlets.util.Enumerator; -import org.apache.tomcat.util.IntrospectionUtils; - -/** - * Based on Wrapper. - * - * Standard implementation of the Wrapper interface that represents - * an individual servlet definition. No child Containers are allowed, and - * the parent Container must be a Context. - * - * @author Craig R. McClanahan - * @author Remy Maucherat - */ -@SuppressWarnings("deprecation") -public class ServletConfigImpl implements ServletConfig, ServletRegistration { - - ServletDynamicRegistration dynamic = new ServletDynamicRegistration(); - - protected boolean asyncSupported; - - private static Logger log= - Logger.getLogger(ServletConfigImpl.class.getName()); - - private static final String[] DEFAULT_SERVLET_METHODS = new String[] { - "GET", "HEAD", "POST" }; - - // TODO: refactor all 'stm' to separate class (not implemented) - // public static final String SINGLE_THREADED_PROXY = - // "org.apache.tomcat.servlets.jsp.SingleThreadedProxyServlet"; - - protected String description; - protected Map initParams = new HashMap(); - protected String servletName; - protected String servletClassName; - protected String jspFile; - protected int loadOnStartup = -1; - protected String runAs; - protected Map securityRoleRef = new HashMap(); // roleName -> [roleLink] - - /** - * The date and time at which this servlet will become available (in - * milliseconds since the epoch), or zero if the servlet is available. - * If this value equals Long.MAX_VALUE, the unavailability of this - * servlet is considered permanent. - */ - private transient long available = 0L; - - private ServletContextImpl ctx; - - /** - * The (single) initialized instance of this servlet. - */ - private transient Servlet instance = null; - - /** - * Are we unloading our servlet instance at the moment? - */ - private transient boolean unloading = false; - - private Class servletClass = null; - - // Support for SingleThreaded - /** - * The count of allocations that are currently active (even if they - * are for the same instance, as will be true on a non-STM servlet). - */ - private transient int countAllocated = 0; - - private transient boolean singleThreadModel = false; - /** - * Stack containing the STM instances. - */ - private transient Stack instancePool = null; - - - // Statistics - private transient long loadTime=0; - private transient int classLoadTime=0; - - // ------------------------------------------------------------- Properties - public ServletConfigImpl(ServletContextImpl ctx, String name, - String classname) { - this.servletName = name; - this.servletClassName = classname; - this.ctx = ctx; - ctx.facade.notifyAdd(this); - } - - /** - * Return the available date/time for this servlet, in milliseconds since - * the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean - * that unavailability is permanent and any request for this servlet will return - * an SC_NOT_FOUND error. If this date/time is in the future, any request for - * this servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero, - * the servlet is currently available. - */ - public long getAvailable() { - return (this.available); - } - - - /** - * Set the available date/time for this servlet, in milliseconds since the - * epoch. If this date/time is Long.MAX_VALUE, it is considered to mean - * that unavailability is permanent and any request for this servlet will return - * an SC_NOT_FOUND error. If this date/time is in the future, any request for - * this servlet will return an SC_SERVICE_UNAVAILABLE error. - * - * @param available The new available date/time - */ - public void setAvailable(long available) { - - long oldAvailable = this.available; - if (available > System.currentTimeMillis()) - this.available = available; - else - this.available = 0L; - - } - - - /** - * Return the number of active allocations of this servlet, even if they - * are all for the same instance (as will be true for servlets that do - * not implement SingleThreadModel. - */ - public int getCountAllocated() { - return (this.countAllocated); - } - - /** - * Return the jsp-file setting for this servlet. - */ - public String getJspFile() { - return jspFile; - } - - public void setJspFile(String s) { - this.jspFile = s; - } - - /** - * Return the load-on-startup order value (negative value means - * load on first call). - */ - public int getLoadOnStartup() { - return loadOnStartup; - } - - /** - * Return the fully qualified servlet class name for this servlet. - */ - public String getServletClass() { - return servletClassName; - } - - /** - * Is this servlet currently unavailable? - */ - public boolean isUnavailable() { - if (available == 0L) - return (false); - else if (available <= System.currentTimeMillis()) { - available = 0L; - return (false); - } else - return (true); - - } - - - /** - * Gets the names of the methods supported by the underlying servlet. - * - * This is the same set of methods included in the Allow response header - * in response to an OPTIONS request method processed by the underlying - * servlet. - * - * @return Array of names of the methods supported by the underlying - * servlet - */ - public String[] getServletMethods() throws ServletException { - - Class servletClazz = loadServlet().getClass(); - if (!javax.servlet.http.HttpServlet.class.isAssignableFrom( - servletClazz)) { - return DEFAULT_SERVLET_METHODS; - } - - HashSet allow = new HashSet(); - allow.add("TRACE"); - allow.add("OPTIONS"); - - Method[] methods = getAllDeclaredMethods(servletClazz); - for (int i=0; methods != null && inull - * to mark this servlet as permanently unavailable - */ - public void unavailable(UnavailableException unavailable) { - getServletContext().log("UnavailableException:" + getServletName()); - if (unavailable == null) - setAvailable(Long.MAX_VALUE); - else if (unavailable.isPermanent()) - setAvailable(Long.MAX_VALUE); - else { - int unavailableSeconds = unavailable.getUnavailableSeconds(); - if (unavailableSeconds <= 0) - unavailableSeconds = 60; // Arbitrary default - setAvailable(System.currentTimeMillis() + - (unavailableSeconds * 1000L)); - } - - } - - - /** - * Unload all initialized instances of this servlet, after calling the - * destroy() method for each instance. This can be used, - * for example, prior to shutting down the entire servlet engine, or - * prior to reloading all of the classes from the Loader associated with - * our Loader's repository. - * - * @exception ServletException if an exception is thrown by the - * destroy() method - */ - public synchronized void unload() throws ServletException { - setAvailable(Long.MAX_VALUE); - - // Nothing to do if we have never loaded the instance - if (!singleThreadModel && (instance == null)) - return; - unloading = true; - - // Loaf a while if the current instance is allocated - // (possibly more than once if non-STM) - if (countAllocated > 0) { - int nRetries = 0; - long delay = ctx.getUnloadDelay() / 20; - while ((nRetries < 21) && (countAllocated > 0)) { - if ((nRetries % 10) == 0) { - log.info("Servlet.unload() timeout " + - countAllocated); - } - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - ; - } - nRetries++; - } - } - - ClassLoader oldCtxClassLoader = - Thread.currentThread().getContextClassLoader(); - if (instance != null) { - ClassLoader classLoader = instance.getClass().getClassLoader(); - - PrintStream out = System.out; - // Call the servlet destroy() method - try { - Thread.currentThread().setContextClassLoader(classLoader); - instance.destroy(); - } catch (Throwable t) { - instance = null; - //instancePool = null; - unloading = false; - throw new ServletException("Servlet.destroy() " + - getServletName(), t); - } finally { - // restore the context ClassLoader - Thread.currentThread().setContextClassLoader(oldCtxClassLoader); - } - - // Deregister the destroyed instance - instance = null; - } - if (singleThreadModel && (instancePool != null)) { - try { - ClassLoader classLoader = ctx.getClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader); - while (!instancePool.isEmpty()) { - ((Servlet) instancePool.pop()).destroy(); - } - } catch (Throwable t) { - instancePool = null; - unloading = false; - throw new ServletException("Servlet.destroy() " + getServletName(), t); - } finally { - // restore the context ClassLoader - Thread.currentThread().setContextClassLoader - (oldCtxClassLoader); - } - instancePool = null; - } - - singleThreadModel = false; - - unloading = false; - } - - - /** - * Return the initialization parameter value for the specified name, - * if any; otherwise return null. - * - * @param name Name of the initialization parameter to retrieve - */ - public String getInitParameter(String name) { - return initParams.get(name); - } - - - /** - * Return the set of initialization parameter names defined for this - * servlet. If none are defined, an empty Enumeration is returned. - */ - public Enumeration getInitParameterNames() { - synchronized (initParams) { - return (new Enumerator(initParams.keySet())); - } - } - - - /** - * Return the servlet context with which this servlet is associated. - */ - public ServletContext getServletContext() { - return ctx; - } - - - /** - * Return the name of this servlet. - */ - public String getServletName() { - return servletName; - } - -// public long getProcessingTime() { -// return swValve.getProcessingTime(); -// } -// -// public void setProcessingTime(long processingTime) { -// swValve.setProcessingTime(processingTime); -// } -// -// public long getMaxTime() { -// return swValve.getMaxTime(); -// } -// -// public void setMaxTime(long maxTime) { -// swValve.setMaxTime(maxTime); -// } -// -// public long getMinTime() { -// return swValve.getMinTime(); -// } -// -// public void setMinTime(long minTime) { -// swValve.setMinTime(minTime); -// } -// -// public int getRequestCount() { -// return swValve.getRequestCount(); -// } -// -// public void setRequestCount(int requestCount) { -// swValve.setRequestCount(requestCount); -// } -// -// public int getErrorCount() { -// return swValve.getErrorCount(); -// } -// -// public void setErrorCount(int errorCount) { -// swValve.setErrorCount(errorCount); -// } -// -// /** -// * Increment the error count used for monitoring. -// */ -// public void incrementErrorCount(){ -// swValve.setErrorCount(swValve.getErrorCount() + 1); -// } -// -// public long getLoadTime() { -// return loadTime; -// } -// -// public void setLoadTime(long loadTime) { -// this.loadTime = loadTime; -// } -// -// public int getClassLoadTime() { -// return classLoadTime; -// } - - // -------------------------------------------------------- Package Methods - - - // -------------------------------------------------------- Private Methods - - private Method[] getAllDeclaredMethods(Class c) { - - if (c.equals(javax.servlet.http.HttpServlet.class)) { - return null; - } - - Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass()); - - Method[] thisMethods = c.getDeclaredMethods(); - if (thisMethods == null) { - return parentMethods; - } - - if ((parentMethods != null) && (parentMethods.length > 0)) { - Method[] allMethods = - new Method[parentMethods.length + thisMethods.length]; - System.arraycopy(parentMethods, 0, allMethods, 0, - parentMethods.length); - System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, - thisMethods.length); - - thisMethods = allMethods; - } - - return thisMethods; - } - - /** Specify the instance. Avoids the class lookup, disables unloading. - * Use for embedded case, or to control the allocation. - * - * @param servlet - */ - public void setServlet(Servlet servlet) { - instance = servlet; - ctx.getObjectManager().bind("Servlet:" + - ctx.getContextPath() + ":" + getServletName(), - this); - } - - public String getSecurityRoleRef(String role) { - return (String)securityRoleRef.get(role); - } - - public void setSecurityRoleRef(Map securityRoles) { - this.securityRoleRef = securityRoles; - } - - public void setConfig(Map initParams) { - this.initParams = initParams; - } - - public void setLoadOnStartup(int loadOnStartup) { - this.loadOnStartup = loadOnStartup; - } - - @Override - public Set addMapping(String... urlPatterns) { - if (ctx.startDone) { - // Use the context method instead of the servlet API to - // add mappings after context init. - throw new IllegalStateException(); - } - Set failed = new HashSet(); - for (String url: urlPatterns) { - if (url == null) { - throw new IllegalArgumentException(); - } - if (ctx.contextConfig.servletMapping.get(url) != null) { - failed.add(url); - } else { - ctx.contextConfig.servletMapping.put(url, getServletName()); - ctx.addMapping(url, this); - } - } - return failed; - } - - - @Override - public boolean setInitParameter(String name, String value) - throws IllegalArgumentException, IllegalStateException { - return ServletContextImpl.setInitParameter(ctx, initParams, - name, value); - } - - - @Override - public Set setInitParameters(Map initParameters) - throws IllegalArgumentException, IllegalStateException { - return ServletContextImpl.setInitParameters(ctx, initParams, - initParameters); - } - - public Dynamic getDynamic() { - return dynamic; - } - - class ServletDynamicRegistration implements Dynamic { - - - @Override - public void setAsyncSupported(boolean isAsyncSupported) - throws IllegalStateException { - asyncSupported = isAsyncSupported; - } - - - public void setDescription(String description) - throws IllegalStateException { - ServletConfigImpl.this.description = description; - } - - @Override - public Set setServletSecurity(ServletSecurityElement constraint) { - //implement me - return null; - } - - @Override - public void setLoadOnStartup(int loadOnStartup) { - //implement me - here to compile - } - - @Override - public void setMultipartConfig(MultipartConfigElement multipartConfig) { - //implement me - here to compile - } - - @Override - public void setRunAsRole(String roleName) { - //implement me - here to compile - } - - @Override - public String getRunAsRole() { - //implement me - here to compile - return null; - } - - @Override - public Collection getMappings() { - //implement me - return null; - } - - @Override - public Set addMapping(String... urlPatterns) { - //implement me - return null; - } - - @Override - public Map getInitParameters() { - // implement me - return null; - } - - @Override - public Set setInitParameters(Map initParameters) - throws IllegalArgumentException, IllegalStateException { - // implement me - return null; - } - - @Override - public String getClassName() { - // implement me - return null; - } - - @Override - public String getName() { - // implement me - return null; - } - - @Override - public String getInitParameter(String name) { - // implement me - return null; - } - - @Override - public boolean setInitParameter(String name, String value) - throws IllegalArgumentException, IllegalStateException { - // implement me - return false; - } - - } - - @Override - public Collection getMappings() { - //implement me - return null; - } - - public void setServletClass(Class servletClass2) { - servletClass = servletClass2; - } - - - @Override - public String getRunAsRole() { - //implement me - return null; - } - - - @Override - public Map getInitParameters() { - // implement me - return null; - } - - @Override - public String getClassName() { - // implement me - return null; - } - - @Override - public String getName() { - // implement me - return null; - } - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletContextImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletContextImpl.java deleted file mode 100644 index 7157cded9..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletContextImpl.java +++ /dev/null @@ -1,1628 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Enumeration; -import java.util.EventListener; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.servlet.DispatcherType; -import javax.servlet.Filter; -import javax.servlet.FilterRegistration; -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletContextAttributeEvent; -import javax.servlet.ServletContextAttributeListener; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; -import javax.servlet.SessionCookieConfig; -import javax.servlet.SessionTrackingMode; -import javax.servlet.FilterRegistration.Dynamic; -import javax.servlet.descriptor.JspConfigDescriptor; - -import org.apache.tomcat.addons.UserSessionManager; -import org.apache.tomcat.integration.ObjectManager; -import org.apache.tomcat.lite.webxml.ServletContextConfig; -import org.apache.tomcat.lite.webxml.ServletContextConfig.FilterData; -import org.apache.tomcat.lite.webxml.ServletContextConfig.FilterMappingData; -import org.apache.tomcat.lite.webxml.ServletContextConfig.ServletData; -import org.apache.tomcat.servlets.util.Enumerator; -import org.apache.tomcat.servlets.util.RequestUtil; -import org.apache.tomcat.servlets.util.UrlUtils; -import org.apache.tomcat.util.http.MimeMap; - - -/** - * Context - initialized from web.xml or using APIs. - * - * Initialization order: - * - * - add all listeners - * - add all filters - * - add all servlets - * - * - session parameters - * - - * - * @author Craig R. McClanahan - * @author Remy Maucherat - * @version $Revision$ $Date$ - */ - -public class ServletContextImpl implements ServletContext { - - /** - * Empty collection to serve as the basis for empty enumerations. - */ - private transient static final ArrayList empty = new ArrayList(); - - transient Logger log; - - /** - * Base path - the directory root of the webapp - */ - protected String basePath = null; - - protected String contextPath; - - // All config from web.xml - protected ServletContextConfig contextConfig = new ServletContextConfig(); - - MimeMap contentTypes = new MimeMap(); - - /** - * The context attributes for this context. - */ - protected transient Map attributes = new HashMap(); - - /** - * List of read only attributes for this context. - * In catalina - used to protect workdir att. We trust the app, so no need - * for extra complexity. - */ - //protected transient HashMap readOnlyAttributes = new HashMap(); - - protected transient ArrayList lifecycleListeners = new ArrayList(); - - protected UserSessionManager manager; - - HashMap filters = new HashMap(); - - HashMap servlets = new HashMap(); - - ArrayList securityRoles = new ArrayList(); - - /** Mapper for filters. - */ - protected WebappFilterMapper webappFilterMapper; - - /** Internal mapper for request dispatcher, must have all - * context mappings. - */ - protected WebappServletMapper mapper; - - transient Locale2Charset charsetMapper = new Locale2Charset(); - - transient TomcatLite facade; - - ObjectManager om; - - private String hostname; - - - boolean initDone = false; - - boolean startDone = false; - - // ------------------------------------------------- ServletContext Methods - public ServletContextImpl() { - } - - public void setTomcat(TomcatLite facade) { - this.facade = facade; - } - - /** - * Registry/framework interface associated with the context. - * Also available as a context attribute. - * @return - */ - public ObjectManager getObjectManager() { - if (om == null) { - om = facade.getObjectManager(); - } - return om; - } - - public void setObjectManager(ObjectManager om) { - this.om = om; - } - - public Locale2Charset getCharsetMapper() { - return charsetMapper; - } - - /** - * Set the context path, starting with "/" - "/" for ROOT - * @param path - */ - public void setContextPath(String path) { - this.contextPath = path; - log = Logger.getLogger("webapp" + path.replace('/', '.')); - } - - public void setHostname(String hostname) { - this.hostname = hostname; - } - - public String getHostname() { - return hostname; - } - - /** The directory where this app is based. May be null. - * - * @param basePath - */ - public void setBasePath(String basePath) { - this.basePath = basePath; - } - - public ServletContextConfig getContextConfig() { - return contextConfig; - } - - /** The directory where this app is based. - * - * @param basePath - */ - public String getBasePath() { - return basePath; - } - - public String getEncodedPath() { - return null; - } - - - public boolean getCookies() { - return false; - } - - - public ServletContext getServletContext() { - return this; - } - - public List getListeners() { - return lifecycleListeners; - } - - public void addListener(Class listenerClass) { - // implement me - } - - public void addListener(String className) { - // implement me - } - - public void addListener(T t) { - lifecycleListeners.add(t); - } - - - public void removeListener(EventListener listener) { - lifecycleListeners.remove(listener); - } - - - - public Logger getLogger() { - return log; - } - - public long getUnloadDelay() { - return 0; - } - - public ServletConfigImpl getServletConfig(String jsp_servlet_name) { - return (ServletConfigImpl)servlets.get(jsp_servlet_name); - } - - public Map getServletConfigs() { - return servlets; - } - - /** - * Add a servlet to the context. - * Called from processWebAppData() - * - * @param servletConfig - */ - public void addServletConfig(ServletConfigImpl servletConfig) { - servlets.put(servletConfig.getServletName(), servletConfig); - } - - public boolean getPrivileged() { - return false; - } - - - public Map getFilters() { - return filters; - } - - - protected boolean getCrossContext() { - return true; - } - - public void addMimeType(String ext, String type) { - contentTypes.addContentType(ext, type); - } - - public WebappServletMapper getMapper() { - if (mapper == null) { - Object customMapper = getObjectManager().get(WebappServletMapper.class); - if (customMapper == null) { - mapper = new WebappServletMapper(); - } else { - mapper = (WebappServletMapper) customMapper; - } - mapper.setServletContext(this); - } - - return mapper; - } - - public WebappFilterMapper getFilterMapper() { - if (webappFilterMapper == null) { - Object customMapper = getObjectManager().get(WebappFilterMapper.class); - if (customMapper == null) { - webappFilterMapper = new WebappFilterMapper(); - } else { - webappFilterMapper = (WebappFilterMapper) customMapper; - } - webappFilterMapper.setServletContext(this); - } - - return webappFilterMapper ; - } - - public FilterConfigImpl getFilter(String name) { - return (FilterConfigImpl)filters.get(name); - } - - /** - * Return the value of the specified context attribute, if any; - * otherwise return null. - * - * @param name Name of the context attribute to return - */ - public Object getAttribute(String name) { - if ("ObjectManager".equals(name)) { - return om; - } - if ("context-listeners".equals(name)) { - return lifecycleListeners; - } - return (attributes.get(name)); - } - - /** - * Return an enumeration of the names of the context attributes - * associated with this context. - */ - public Enumeration getAttributeNames() { - return new Enumerator(attributes.keySet(), true); - } - - - public void addSecurityRole(String role) { - securityRoles.add(role); - } - - public List getSecurityRoles() { - return securityRoles; - } - - /** - * Return a ServletContext object that corresponds to a - * specified URI on the server. This method allows servlets to gain - * access to the context for various parts of the server, and as needed - * obtain RequestDispatcher objects or resources from the - * context. The given path must be absolute (beginning with a "/"), - * and is interpreted based on our virtual host's document root. - * - * @param uri Absolute URI of a resource on the server - */ - public ServletContext getContext(String uri) { - // TODO: support real uri ( http://host/path ) - // Validate the format of the specified argument - if ((uri == null) || (!uri.startsWith("/"))) - return (null); - - ServletContextImpl child = null; - try { - child = facade.getContext(this, uri); - } catch (IOException e) { - } catch (ServletException e) { - } - - if (child == null) - return (null); - - if (this.getCrossContext()) { - // If crossContext is enabled, can always return the context - return child.getServletContext(); - } else if (child == this) { - // Can still return the current context - return this.getServletContext(); - } else { - // Nothing to return - return (null); - } - } - - - /** - * Return the main path associated with this context. - */ - public String getContextPath() { - return contextPath; - } - - - /** - * Return the value of the specified initialization parameter, or - * null if this parameter does not exist. - * - * @param name Name of the initialization parameter to retrieve - */ - public String getInitParameter(final String name) { - return ((String) contextConfig.contextParam.get(name)); - } - - - /** - * Return the names of the context's initialization parameters, or an - * empty enumeration if the context has no initialization parameters. - */ - public Enumeration getInitParameterNames() { - return (new Enumerator(contextConfig.contextParam.keySet())); - } - - public void setContextParams(Map newParams) { - contextConfig.contextParam = (HashMap) newParams; - } - - /** - * Return the major version of the Java Servlet API that we implement. - */ - public int getMajorVersion() { - return 2; - } - - - /** - * Return the minor version of the Java Servlet API that we implement. - */ - public int getMinorVersion() { - return 4; - } - - - /** - * Return the MIME type of the specified file, or null if - * the MIME type cannot be determined. - * - * @param file Filename for which to identify a MIME type - */ - public String getMimeType(String file) { - return contentTypes.getMimeType(file); - } - - /** - * Return the real path for a given virtual path, if possible; otherwise - * return null. - * - * @param path The path to the desired resource - */ - public String getRealPath(String path) { - if (path == null) { - return null; - } - - File file = new File(basePath, path); - return (file.getAbsolutePath()); - } - - /** - * Return a RequestDispatcher object that acts as a - * wrapper for the named servlet. - * - * @param name Name of the servlet for which a dispatcher is requested - */ - public RequestDispatcher getNamedDispatcher(String name) { - if (name == null) return null; - ServletConfigImpl wrapper = - (ServletConfigImpl) this.getServletConfig(name); - if (wrapper == null) return null; - - return new RequestDispatcherImpl(wrapper, name); - } - - - /** - * Return a RequestDispatcher instance that acts as a - * wrapper for the resource at the given path. The path must begin - * with a "/" and is interpreted as relative to the current context root. - * - * @param path The path to the desired resource. - */ - public RequestDispatcher getRequestDispatcher(String path) { - if (path == null) return null; - - if (!path.startsWith("/")) - throw new IllegalArgumentException(path); - - path = UrlUtils.normalize(path); - if (path == null) return (null); - - - return new RequestDispatcherImpl(this, path); - } - - public RequestDispatcher getRequestDispatcher(String path, - int type, - String dispatcherPath) { - RequestDispatcher dispatcher = getRequestDispatcher(path); - //((RequestDispatcherImpl)dispatcher); - return dispatcher; - } - - ThreadLocal requestDispatcherStack = new ThreadLocal(); - - protected ClassLoader classLoader; - -// protected RequestDispatcherImpl getRequestDispatcher() { -// ArrayList/**/ list = -// (ArrayList)requestDispatcherStack.get(); -// if (list == null) { -// list = new ArrayList(); -// requestDispatcherStack.set(list); -// } -// -// -// return null; -// } - - public void resetDispatcherStack() { - - } - - /** - * Return the URL to the resource that is mapped to a specified path. - * The path must begin with a "/" and is interpreted as relative to the - * current context root. - * - * @param path The path to the desired resource - * - * @exception MalformedURLException if the path is not given - * in the correct form - */ - public URL getResource(String path) - throws MalformedURLException { - - if (path == null || !path.startsWith("/")) { - throw new MalformedURLException("getResource() " + path); - } - - path = UrlUtils.normalize(path); - if (path == null) - return (null); - - String libPath = "/WEB-INF/lib/"; - if ((path.startsWith(libPath)) && (path.endsWith(".jar"))) { - File jarFile = null; - jarFile = new File(basePath, path); - if (jarFile.exists()) { - return jarFile.toURL(); - } else { - return null; - } - } else { - File resFile = new File(basePath + path); - if (resFile.exists()) { - return resFile.toURL(); - } - } - - return (null); - - } - - /** - * Return the requested resource as an InputStream. The - * path must be specified according to the rules described under - * getResource. If no such resource can be identified, - * return null. - * - * @param path The path to the desired resource. - */ - public InputStream getResourceAsStream(String path) { - - path = UrlUtils.normalize(path); - if (path == null) - return (null); - - File resFile = new File(basePath + path); - if (!resFile.exists()) - return null; - - try { - return new FileInputStream(resFile); - } catch (FileNotFoundException e) { - return null; - } - - } - - - /** - * Return a Set containing the resource paths of resources member of the - * specified collection. Each path will be a String starting with - * a "/" character. The returned set is immutable. - * - * @param path Collection path - */ - public Set getResourcePaths(String path) { - - // Validate the path argument - if (path == null) { - return null; - } - if (!path.startsWith("/")) { - throw new IllegalArgumentException("getResourcePaths() " + path); - } - - path = UrlUtils.normalize(path); - if (path == null) - return (null); - - File f = new File(basePath + path); - File[] files = f.listFiles(); - if (files == null) return null; - if (!path.endsWith("/")) { - path = path + "/"; - } - - HashSet result = new HashSet(); - for (int i=0; i < files.length; i++) { - if (files[i].isDirectory() ) { - result.add(path + files[i].getName() + "/"); - } else { - result.add(path + files[i].getName()); - } - } - return result; - } - - - - /** - * Return the name and version of the servlet container. - */ - public String getServerInfo() { - return "Apache Tomcat Lite"; - } - - /** - * @deprecated As of Java Servlet API 2.1, with no direct replacement. - */ - public Servlet getServlet(String name) { - return (null); - } - - - /** - * Return the display name of this web application. - */ - public String getServletContextName() { - return contextConfig.displayName; - } - - - /** - * @deprecated As of Java Servlet API 2.1, with no direct replacement. - */ - public Enumeration getServletNames() { - return (new Enumerator(empty)); - } - - - /** - * @deprecated As of Java Servlet API 2.1, with no direct replacement. - */ - public Enumeration getServlets() { - return (new Enumerator(empty)); - } - - - /** - * Writes the specified message to a servlet log file. - * - * @param message Message to be written - */ - public void log(String message) { - this.getLogger().info(message); - } - - - /** - * Writes the specified exception and message to a servlet log file. - * - * @param exception Exception to be reported - * @param message Message to be written - * - * @deprecated As of Java Servlet API 2.1, use - * log(String, Throwable) instead - */ - public void log(Exception exception, String message) { - this.getLogger().log(Level.INFO, message, exception); - } - - - /** - * Writes the specified message and exception to a servlet log file. - * - * @param message Message to be written - * @param throwable Exception to be reported - */ - public void log(String message, Throwable throwable) { - this.getLogger().log(Level.INFO, message, throwable); - } - - /** - * Remove the context attribute with the specified name, if any. - * - * @param name Name of the context attribute to be removed - */ - public void removeAttribute(String name) { - - Object value = null; - boolean found = false; - - // Remove the specified attribute - // Check for read only attribute - found = attributes.containsKey(name); - if (found) { - value = attributes.get(name); - attributes.remove(name); - } else { - return; - } - - // Notify interested application event listeners - List listeners = this.getListeners(); - if (listeners.size() == 0) - return; - ServletContextAttributeEvent event = null; - for (int i = 0; i < listeners.size(); i++) { - if (!(listeners.get(i) instanceof ServletContextAttributeListener)) - continue; - ServletContextAttributeListener listener = - (ServletContextAttributeListener) listeners.get(i); - try { - if (event == null) { - event = new ServletContextAttributeEvent(this.getServletContext(), - name, value); - - } - listener.attributeRemoved(event); - } catch (Throwable t) { - // FIXME - should we do anything besides log these? - log("ServletContextAttributeListener", t); - } - } - } - - - /** - * Bind the specified value with the specified context attribute name, - * replacing any existing value for that name. - * - * @param name Attribute name to be bound - * @param value New attribute value to be bound - */ - public void setAttribute(String name, Object value) { - // Name cannot be null - if (name == null) - throw new IllegalArgumentException - ("name == null"); - - // Null value is the same as removeAttribute() - if (value == null) { - removeAttribute(name); - return; - } - - Object oldValue = null; - boolean replaced = false; - - // Add or replace the specified attribute - synchronized (attributes) { - // Check for read only attribute - oldValue = attributes.get(name); - if (oldValue != null) - replaced = true; - attributes.put(name, value); - } - - // Notify interested application event listeners - List listeners = this.getListeners(); - if (listeners.size() == 0) - return; - ServletContextAttributeEvent event = null; - for (int i = 0; i < listeners.size(); i++) { - if (!(listeners.get(i) instanceof ServletContextAttributeListener)) - continue; - ServletContextAttributeListener listener = - (ServletContextAttributeListener) listeners.get(i); - try { - if (event == null) { - if (replaced) - event = - new ServletContextAttributeEvent(this.getServletContext(), - name, oldValue); - else - event = - new ServletContextAttributeEvent(this.getServletContext(), - name, value); - - } - if (replaced) { - listener.attributeReplaced(event); - } else { - listener.attributeAdded(event); - } - } catch (Throwable t) { - // FIXME - should we do anything besides log these? - log("ServletContextAttributeListener error", t); - } - } - - } - - /** - * Clear all application-created attributes. - */ - void clearAttributes() { - // Create list of attributes to be removed - ArrayList list = new ArrayList(); - synchronized (attributes) { - Iterator iter = attributes.keySet().iterator(); - while (iter.hasNext()) { - list.add(iter.next()); - } - } - - // Remove application originated attributes - // (read only attributes will be left in place) - Iterator keys = list.iterator(); - while (keys.hasNext()) { - String key = (String) keys.next(); - removeAttribute(key); - } - } - - public void initFilters() throws ServletException { - Iterator fI = getFilters().values().iterator(); - while (fI.hasNext()) { - FilterConfigImpl fc = (FilterConfigImpl)fI.next(); - try { - fc.getFilter(); // will triger init() - } catch (Throwable e) { - log.log(Level.WARNING, getContextPath() + " Filter.init() " + - fc.getFilterName(), e); - } - - } - } - - public void initServlets() throws ServletException { - Iterator fI = getServletConfigs().values().iterator(); - Map/*>*/ onStartup = - new TreeMap/*>*/(); - while (fI.hasNext()) { - ServletConfigImpl fc = (ServletConfigImpl)fI.next(); - if (fc.getLoadOnStartup() > 0 ) { - Integer i = new Integer(fc.getLoadOnStartup()); - List/**/ old = (List)onStartup.get(i); - if (old == null) { - old = new ArrayList/**/(); - onStartup.put(i, old); - } - old.add(fc); - } - } - Iterator keys = onStartup.keySet().iterator(); - while (keys.hasNext()) { - Integer key = (Integer)keys.next(); - List/**/ servlets = (List)onStartup.get(key); - Iterator servletsI = servlets.iterator(); - while (servletsI.hasNext()) { - ServletConfigImpl fc = (ServletConfigImpl) servletsI.next(); - try { - fc.loadServlet(); - } catch (Throwable e) { - log.log(Level.WARNING, "Error initializing " + fc.getServletName(), e); - } - } - } - } - - public void initListeners() throws ServletException { - Iterator fI = contextConfig.listenerClass.iterator(); - while (fI.hasNext()) { - String listenerClass = (String)fI.next(); - try { - Object l = - getClassLoader().loadClass(listenerClass).newInstance(); - lifecycleListeners.add((EventListener) l); - } catch (Throwable e) { - log.log(Level.WARNING, "Error initializing listener " + listenerClass, e); - } - } - } - - public ClassLoader getClassLoader() { - return classLoader; - } - - public void addMapping(String path, String name) { - ServletConfigImpl wrapper = getServletConfig(name); - addMapping(path, wrapper); - } - - public void addMapping(String path, ServletConfig wrapper) { - getMapper().addWrapper(getMapper().contextMapElement, path, wrapper); - } - - - - public void setWelcomeFiles(String[] name) { - getMapper().contextMapElement.welcomeResources = name; - } - - public String[] getWelcomeFiles() { - return getMapper().contextMapElement.welcomeResources; - } - - public void setSessionTimeout(int to) { - getManager().setSessionTimeout(to); - } - - /** - * Initialize the context from the parsed config. - * - * Note that WebAppData is serializable. - */ - public void processWebAppData(ServletContextConfig d) throws ServletException { - this.contextConfig = d; - - for (String k: d.mimeMapping.keySet()) { - addMimeType(k, d.mimeMapping.get(k)); - } - - String[] wFiles = (String[])d.welcomeFileList.toArray(new String[0]); - if (wFiles.length == 0) { - wFiles = new String[] {"index.html" }; - } - if (basePath != null) { - getMapper().contextMapElement.resources = new File(getBasePath()); - } - setWelcomeFiles(wFiles); - - Iterator i2 = d.filters.values().iterator(); - while (i2.hasNext()) { - FilterData fd = (FilterData)i2.next(); - addFilter(fd.filterName, fd.filterClass, fd.initParams); - } - - Iterator i3 = d.servlets.values().iterator(); - while (i3.hasNext()) { - ServletData sd = (ServletData) i3.next(); - // jsp-file - if (sd.servletClass == null) { - if (sd.jspFile == null) { - log.log(Level.WARNING, "Missing servlet class for " + sd.servletName); - continue; - } - } - - ServletConfigImpl sw = - new ServletConfigImpl(this, sd.servletName, sd.servletClass); - sw.setConfig(sd.initParams); - sw.setJspFile(sd.jspFile); - sw.setLoadOnStartup(sd.loadOnStartup); - //sw.setRunAs(sd.runAs); - sw.setSecurityRoleRef(sd.securityRoleRef); - - addServletConfig(sw); - } - - for (String k: d.servletMapping.keySet()) { - addMapping(k, d.servletMapping.get(k)); - } - - Iterator i5 = d.filterMappings.iterator(); - while (i5.hasNext()) { - FilterMappingData k = (FilterMappingData) i5.next(); - String[] disp = new String[k.dispatcher.size()]; - if (k.urlPattern != null) { - addFilterMapping(k.urlPattern, - k.filterName, - (String[])k.dispatcher.toArray(disp)); - } - if (k.servletName != null) { - addFilterServletMapping(k.servletName, - k.filterName, - (String[])k.dispatcher.toArray(disp)); - } - } - - for (String n: d.localeEncodingMapping.keySet()) { - getCharsetMapper().addCharsetMapping(n, - d.localeEncodingMapping.get(n)); - } - } - - public void addServlet(String servletName, String servletClass, - String jspFile, Map params) { - ServletConfigImpl sc = new ServletConfigImpl(this, servletName, - servletClass); - sc.setJspFile(jspFile); - sc.setConfig(params); - addServletConfig(sc); - } - - @Override - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { - ServletConfigImpl sc = new ServletConfigImpl(this, servletName, null); - sc.setServlet(servlet); - addServletConfig(sc); - return sc.getDynamic(); - } - - public void addServletSec(String serlvetName, String runAs, Map roles) { - // TODO - } - - - - public void addFilterMapping(String path, String filterName, - String[] dispatcher) { - getFilterMapper().addMapping(filterName, - path, null, dispatcher, true); - - } - - public void addFilterServletMapping(String servlet, - String filterName, - String[] dispatcher) { - getFilterMapper().addMapping(filterName, - null, servlet, - dispatcher, true); - } - - /** - * Called from TomcatLite.init(), required before start. - * - * Will initialize defaults and load web.xml unless webAppData is - * already set and recent. No other processing is done except reading - * the config - you can add or alter it before start() is called. - * - * @throws ServletException - */ - public void init() throws ServletException { - if (initDone) { - return; - } - initDone = true; - // Load global init params from the facade - initEngineDefaults(); - - initTempDir(); - - - // Merge in web.xml - or other config source ( programmatic, etc ) - ContextPreinitListener cfg = - (ContextPreinitListener) getObjectManager().get( - ContextPreinitListener.class); - if (cfg != null) { - cfg.preInit(this); - } - - processWebAppData(contextConfig); - - // if not defined yet: - addDefaultServlets(); - } - - - protected void initTempDir() throws ServletException { - // We need a base path - at least for temp files, req. by spec - if (basePath == null) { - basePath = ("/".equals(contextPath)) ? - facade.getWork().getAbsolutePath() + "/ROOT" : - facade.getWork().getAbsolutePath() + contextPath; - } - - File f = new File(basePath + "/WEB-INF/tmp"); - f.mkdirs(); - setAttribute("javax.servlet.context.tempdir", f); - } - - /** - * Static file handler ( default ) - * *.jsp support - * - */ - protected void addDefaultServlets() throws ServletException { - if (servlets.get("default") == null) { - ServletConfigImpl fileS = new ServletConfigImpl(this, - "default", null); - addServletConfig(fileS); - addMapping("/", fileS); - } - - // *.jsp support - if (servlets.get("jspwildcard") == null) { - ServletConfigImpl fileS = new ServletConfigImpl(this, - "jspwildcard", null); - addServletConfig(fileS); - addMapping("*.jsp", fileS); - } - } - - protected void initEngineDefaults() throws ServletException { - - // TODO: make this customizable, avoid loading it on startup - // Set the class name as default in the addon support - for (String sname: facade.ctxDefaultInitParam.keySet()) { - String path = facade.ctxDefaultInitParam.get(sname); - contextConfig.contextParam.put(sname, path); - } - - for (String sname: facade.preloadServlets.keySet()) { - String sclass = facade.preloadServlets.get(sname); - ServletConfigImpl fileS = new ServletConfigImpl(this, sname, sclass); - addServletConfig(fileS); - } - - for (String sname: facade.preloadMappings.keySet()) { - String path = facade.preloadMappings.get(sname); - ServletConfigImpl servletConfig = getServletConfig(sname); - addMapping(path, servletConfig); - } - } - - - public ArrayList getClasspath(File directory, File classesDir) { - ArrayList res = new ArrayList(); - if (classesDir.isDirectory() && classesDir.exists() && - classesDir.canRead()) { - try { - URL url = classesDir.toURL(); - res.add(url); - } catch (MalformedURLException e) { - } - } - if (!directory.isDirectory() || !directory.exists() - || !directory.canRead()) { - return res; - } - - File[] jars = directory.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.endsWith(".jar"); - } - }); - - for (int j = 0; j < jars.length; j++) { - try { - URL url = jars[j].toURL(); - res.add(url); - } catch (MalformedURLException e) { - } - } - return res; - } - - - public void start() throws ServletException { - if (startDone) { - return; - } - String base = getBasePath(); - - ArrayList urls = getClasspath(new File(base + "/WEB-INF/lib"), - new File(base + "/WEB-INF/classes")); - URL[] urlsA = new URL[urls.size()]; - urls.toArray(urlsA); - - URLClassLoader parentLoader = - getEngine().getContextParentLoader(); - - // create a class loader. - // TODO: reimplement special 'deploy' dirs - - /* - Repository ctxRepo = new Repository(); - ctxRepo.setParentClassLoader(parentLoader); - ctxRepo.addURL(urlsA); - repository = ctxRepo; - */ - - classLoader = new URLClassLoader(urlsA, parentLoader); - - // JMX should know about us ( TODO: is it too early ? ) - facade.notifyAdd(this); - - initListeners(); - - List listeners = this.getListeners(); - ServletContextEvent event = null; - for (int i = 0; i < listeners.size(); i++) { - if (!(listeners.get(i) instanceof ServletContextListener)) - continue; - ServletContextListener listener = - (ServletContextListener) listeners.get(i); - if (event == null) { - event = new ServletContextEvent(this); - } - try { - // May add servlets/filters - listener.contextInitialized(event); - } catch (Throwable t) { - log.log(Level.WARNING, "Context.init() contextInitialized() error:", t); - } - } - - - initFilters(); - initServlets(); - - startDone = true; - } - - public UserSessionManager getManager() { - if (manager == null) { - manager = (UserSessionManager) getObjectManager().get( - UserSessionManager.class); - manager.setContext(this); - if (contextConfig.sessionTimeout > 0 ) { - manager.setSessionTimeout(contextConfig.sessionTimeout); - } - } - return manager; - } - - - // TODO: configurable ? init-params - public String getSessionCookieName() { - return "JSESSIONID"; - } - - - - public void destroy() throws ServletException { - // destroy filters - Iterator fI = filters.values().iterator(); - while(fI.hasNext()) { - FilterConfigImpl fc = (FilterConfigImpl) fI.next(); - try { - fc.getFilter().destroy(); - } catch (Exception e) { - e.printStackTrace(); - } - } - // destroy servlets - fI = servlets.values().iterator(); - while(fI.hasNext()) { - ServletConfigImpl fc = (ServletConfigImpl) fI.next(); - try { - fc.unload(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public TomcatLite getEngine() { - return facade; - } - - public String findStatusPage(int status) { - if (contextConfig.errorPageCode.size() == 0) { - return null; - } - if (status == 200) { - return null; - } - - return (String) contextConfig.errorPageCode.get(Integer.toString(status)); - } - - public void handleStatusPage(ServletRequestImpl req, - ServletResponseImpl res, - int status, - String statusPage) { - String message = RequestUtil.filter(res.getMessage()); - if (message == null) - message = ""; - setErrorAttributes(req, status, message); - dispatchError(req, res, statusPage); - } - - protected void setErrorAttributes(ServletRequestImpl req, - int status, - String message) { - req.setAttribute("javax.servlet.error.status_code", - new Integer(status)); - if (req.getWrapper() != null) { - req.setAttribute("javax.servlet.error.servlet_name", - req.getWrapper().servletName); - } - req.setAttribute("javax.servlet.error.request_uri", - req.getRequestURI()); - req.setAttribute("javax.servlet.error.message", - message); - - } - - public void handleError(ServletRequestImpl req, - ServletResponseImpl res, - Throwable t) { - Throwable realError = t; - if (realError instanceof ServletException) { - realError = ((ServletException) realError).getRootCause(); - if (realError == null) { - realError = t; - } - } - //if (realError instanceof ClientAbortException ) { - - String errorPage = findErrorPage(t); - if ((errorPage == null) && (realError != t)) { - errorPage = findErrorPage(realError); - } - - if (errorPage != null) { - setErrorAttributes(req, 500, t.getMessage()); - req.setAttribute("javax.servlet.error.exception", realError); - req.setAttribute("javax.servlet.error.exception_type", - realError.getClass()); - dispatchError(req, res, errorPage); - } else { - log("Unhandled error", t); - if (t instanceof ServletException && - ((ServletException)t).getRootCause() != null) { - log("RootCause:", ((ServletException)t).getRootCause()); - } - if (res.getStatus() < 500) { - res.setStatus(500); - } - } - } - - protected void dispatchError(ServletRequestImpl req, - ServletResponseImpl res, - String errorPage) { - RequestDispatcher rd = - getRequestDispatcher(errorPage); - try { - // will clean up the buffer - rd.forward(req, res); - return; // handled - } catch (ServletException e) { - // TODO - } catch (IOException e) { - // TODO - } - } - - protected String findErrorPage(Throwable exception) { - if (contextConfig.errorPageException.size() == 0) { - return null; - } - if (exception == null) - return (null); - Class clazz = exception.getClass(); - String name = clazz.getName(); - while (!Object.class.equals(clazz)) { - String page = (String)contextConfig.errorPageException.get(name); - if (page != null) - return (page); - clazz = clazz.getSuperclass(); - if (clazz == null) - break; - name = clazz.getName(); - } - return (null); - - } - - - @Override - public EnumSet getDefaultSessionTrackingModes() { - return null; - } - - - @Override - public EnumSet getEffectiveSessionTrackingModes() { - return null; - } - - - @Override - public SessionCookieConfig getSessionCookieConfig() { - return null; - } - - - @Override - public void setSessionTrackingModes(EnumSet sessionTrackingModes) { - } - - - public void addFilter(String filterName, String filterClass, - Map params) { - FilterConfigImpl fc = new FilterConfigImpl(this); - fc.setData(filterName, filterClass, params); - filters.put(filterName, fc); - } - - - @Override - public Dynamic addFilter(String filterName, String className) { - FilterConfigImpl fc = new FilterConfigImpl(this); - fc.setData(filterName, className, new HashMap()); - filters.put(filterName, fc); - return fc.getDynamic(); - } - - - @Override - public Dynamic addFilter(String filterName, Filter filter) { - FilterConfigImpl fc = new FilterConfigImpl(this); - fc.setData(filterName, null, new HashMap()); - fc.setFilter(filter); - filters.put(filterName, fc); - return fc.getDynamic(); - } - - - @Override - public Dynamic addFilter(String filterName, Class filterClass) { - FilterConfigImpl fc = new FilterConfigImpl(this); - fc.setData(filterName, null, new HashMap()); - fc.setFilterClass(filterClass); - filters.put(filterName, fc); - return fc.getDynamic(); - } - - - @Override - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, - String className) { - ServletConfigImpl sc = new ServletConfigImpl(this, servletName, className); - addServletConfig(sc); - return sc.getDynamic(); - } - - - @Override - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, - Class servletClass) { - ServletConfigImpl sc = new ServletConfigImpl(this, servletName, servletClass.getName()); - sc.setServletClass(servletClass); - addServletConfig(sc); - return sc.getDynamic(); - } - - // That's tricky - this filter will have no name. We need to generate one - // because our code relies on names. - AtomicInteger autoName = new AtomicInteger(); - - - @Override - public T createFilter(Class c) throws ServletException { - FilterConfigImpl fc = new FilterConfigImpl(this); - String filterName = "_tomcat_auto_filter_" + autoName.incrementAndGet(); - fc.setData(filterName, null, new HashMap()); - fc.setFilterClass(c); - filters.put(filterName, fc); - - try { - return (T) fc.createFilter(); - } catch (ClassCastException e) { - throw new ServletException(e); - } catch (ClassNotFoundException e) { - throw new ServletException(e); - } catch (IllegalAccessException e) { - throw new ServletException(e); - } catch (InstantiationException e) { - throw new ServletException(e); - } - } - - - @Override - public T createServlet(Class c) throws ServletException { - String filterName = "_tomcat_auto_servlet_" + autoName.incrementAndGet(); - ServletConfigImpl fc = new ServletConfigImpl(this, filterName, null); - fc.setServletClass(c); - servlets.put(filterName, fc); - - try { - return (T) fc.newInstance(); - } catch (ClassCastException e) { - throw new ServletException(e); - } - } - - - public FilterRegistration findFilterRegistration(String filterName) { - return filters.get(filterName); - } - - - public ServletRegistration findServletRegistration(String servletName) { - return servlets.get(servletName); - } - - - @Override - public boolean setInitParameter(String name, String value) { - HashMap params = contextConfig.contextParam; - return setInitParameter(this, params, name, value); - } - - - static Set setInitParameters(ServletContextImpl ctx, - Map params, - Map initParameters) - throws IllegalArgumentException, IllegalStateException { - if (ctx.startDone) { - throw new IllegalStateException(); - } - Set result = new HashSet(); - for (String name: initParameters.keySet()) { - String value = initParameters.get(name); - if (name == null || value == null) { - throw new IllegalArgumentException(); - } - if (!setInitParameter(ctx, params, name, value)) { - result.add(name); - } - } - return result; - } - - /** - * true if the context initialization parameter with the given name and value was set successfully on this ServletContext, and false if it was not set because this ServletContext already contains a context initialization parameter with a matching name - * Throws: - * java.lang.IllegalStateException - if this ServletContext has already been initialized - */ - static boolean setInitParameter(ServletContextImpl ctx, Map params, - String name, String value) { - if (name == null || value == null) { - throw new IllegalArgumentException(); - } - if (ctx.startDone) { - throw new IllegalStateException(); - } - String oldValue = params.get(name); - if (oldValue != null) { - return false; - } else { - params.put(name, value); - return true; - } - } - - public JspConfigDescriptor getJspConfigDescriptor() { - // fix me - just here to compile - return null; - } - - - - public void declareRoles(String... roleNames) { - // implement me - } - - public T createListener(Class c) throws ServletException { - // implement me - return null; - } - - public Collection getMappings() { - // implement me - return null; - } - - public Map getFilterRegistrations() { - // implement me - return null; - } - - public FilterRegistration getFilterRegistration(String filterName) { - // implement me - return null; - } - - public Map getServletRegistrations() { - // implement me - return null; - } - - public ServletRegistration getServletRegistration(String servletName) { - // implement me - return null; - } - - public int getEffectiveMinorVersion() { - // implement me - return -1; - } - - public int getEffectiveMajorVersion() { - // implement me - return -1; - } - -} - diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletInputStreamImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletInputStreamImpl.java deleted file mode 100644 index 5f5d914bc..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletInputStreamImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.tomcat.lite; - -import java.io.IOException; -import java.io.InputStream; - -import javax.servlet.ServletInputStream; - - -/** - * Wrapper around MessageReader - * - * @author Remy Maucherat - * @author Jean-Francois Arcand - * @author Costin Manolache - */ -public class ServletInputStreamImpl extends ServletInputStream { - - - // ----------------------------------------------------- Instance Variables - - - protected InputStream ib; - - - // ----------------------------------------------------------- Constructors - - - public ServletInputStreamImpl(InputStream ib) { - this.ib = ib; - } - - // --------------------------------------------- ServletInputStream Methods - - public long skip(long n) - throws IOException { - return ib.skip(n); - } - - public void mark(int readAheadLimit) - { - //try { - ib.mark(readAheadLimit); -// } catch (IOException e) { -// e.printStackTrace(); -// } - } - - - public void reset() - throws IOException { - ib.reset(); - } - - - - public int read() - throws IOException { - return ib.read(); - } - - public int available() throws IOException { - return ib.available(); - } - - public int read(final byte[] b) throws IOException { - return ib.read(b, 0, b.length); - } - - - public int read(final byte[] b, final int off, final int len) - throws IOException { - - return ib.read(b, off, len); - } - - - public int readLine(byte[] b, int off, int len) throws IOException { - return super.readLine(b, off, len); - } - - /** - * Close the stream - * Since we re-cycle, we can't allow the call to super.close() - * which would permantely disable us. - */ - public void close() throws IOException { - ib.close(); - } - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletOutputStreamImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletOutputStreamImpl.java deleted file mode 100644 index 53ae9d04a..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletOutputStreamImpl.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.tomcat.lite; - -import java.io.IOException; - -import javax.servlet.ServletOutputStream; - - -/** - * Coyote implementation of the servlet output stream. - * - * @author Costin Manolache - * @author Remy Maucherat - */ -public class ServletOutputStreamImpl - extends ServletOutputStream { - - - // ----------------------------------------------------- Instance Variables - - - protected BodyWriter ob; - - - // ----------------------------------------------------------- Constructors - - - public ServletOutputStreamImpl(BodyWriter ob) { - this.ob = ob; - } - - - // --------------------------------------------------------- Public Methods - - - /** - * Prevent cloning the facade. - */ - protected Object clone() - throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } - - - // -------------------------------------------------------- Package Methods - - - /** - * Clear facade. - */ - void clear() { - ob = null; - } - - - // --------------------------------------------------- OutputStream Methods - - - public void write(int i) - throws IOException { - ob.writeByte(i); - } - - - public void write(byte[] b) - throws IOException { - write(b, 0, b.length); - } - - - public void write(byte[] b, int off, int len) - throws IOException { - ob.write(b, off, len); - } - - - /** - * Will send the buffer to the client. - */ - public void flush() - throws IOException { - ob.flush(); - } - - - public void close() - throws IOException { - ob.close(); - } - - - // -------------------------------------------- ServletOutputStream Methods - - - public void print(String s) - throws IOException { - ob.write(s); - } - - -} - diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletReaderImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletReaderImpl.java deleted file mode 100644 index 3bb3308b4..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletReaderImpl.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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.tomcat.lite; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; - - - -/** - * Coyote implementation of the buffred reader. - * - * @author Remy Maucherat - */ -public class ServletReaderImpl - extends BufferedReader { - - - // -------------------------------------------------------------- Constants - - - private static final char[] LINE_SEP = { '\r', '\n' }; - private static final int MAX_LINE_LENGTH = 4096; - - - // ----------------------------------------------------- Instance Variables - - - protected Reader ib; - - - protected char[] lineBuffer = null; - - - // ----------------------------------------------------------- Constructors - - - public ServletReaderImpl(Reader ib) { - super(ib, 1); - this.ib = ib; - } - - public void close() - throws IOException { - ib.close(); - } - - - public int read() - throws IOException { - return ib.read(); - } - - - public int read(char[] cbuf) - throws IOException { - return ib.read(cbuf, 0, cbuf.length); - } - - - public int read(char[] cbuf, int off, int len) - throws IOException { - return ib.read(cbuf, off, len); - } - - - public long skip(long n) - throws IOException { - return ib.skip(n); - } - - - public boolean ready() - throws IOException { - return ib.ready(); - } - - - public boolean markSupported() { - return true; - } - - - public void mark(int readAheadLimit) - throws IOException { - ib.mark(readAheadLimit); - } - - - public void reset() - throws IOException { - ib.reset(); - } - - - // TODO: move the readLine functionality to base coyote IO - public String readLine() - throws IOException { - - if (lineBuffer == null) { - lineBuffer = new char[MAX_LINE_LENGTH]; - } - - String result = null; - - int pos = 0; - int end = -1; - int skip = -1; - StringBuilder aggregator = null; - while (end < 0) { - mark(MAX_LINE_LENGTH); - while ((pos < MAX_LINE_LENGTH) && (end < 0)) { - int nRead = read(lineBuffer, pos, MAX_LINE_LENGTH - pos); - if (nRead < 0) { - if (pos == 0) { - return null; - } - end = pos; - skip = pos; - } - for (int i = pos; (i < (pos + nRead)) && (end < 0); i++) { - if (lineBuffer[i] == LINE_SEP[0]) { - end = i; - skip = i + 1; - char nextchar; - if (i == (pos + nRead - 1)) { - nextchar = (char) read(); - } else { - nextchar = lineBuffer[i+1]; - } - if (nextchar == LINE_SEP[1]) { - skip++; - } - } else if (lineBuffer[i] == LINE_SEP[1]) { - end = i; - skip = i + 1; - } - } - if (nRead > 0) { - pos += nRead; - } - } - if (end < 0) { - if (aggregator == null) { - aggregator = new StringBuilder(); - } - aggregator.append(lineBuffer); - pos = 0; - } else { - reset(); - skip(skip); - } - } - - if (aggregator == null) { - result = new String(lineBuffer, 0, end); - } else { - aggregator.append(lineBuffer, 0, end); - result = aggregator.toString(); - } - - return result; - - } -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletRequestImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletRequestImpl.java deleted file mode 100644 index 4d9a6224e..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletRequestImpl.java +++ /dev/null @@ -1,2572 +0,0 @@ -/* - * 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.tomcat.lite; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.security.Principal; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; -import java.util.TreeMap; -import java.util.logging.Level; - -import javax.security.auth.Subject; -import javax.servlet.AsyncContext; -import javax.servlet.AsyncListener; -import javax.servlet.DispatcherType; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletRequestAttributeEvent; -import javax.servlet.ServletRequestAttributeListener; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.Part; - -import org.apache.tomcat.addons.UserSessionManager; -import org.apache.tomcat.servlets.util.Enumerator; -import org.apache.tomcat.servlets.util.LocaleParser; -import org.apache.tomcat.servlets.util.RequestUtil; -import org.apache.tomcat.util.buf.B2CConverter; -import org.apache.tomcat.util.buf.ByteChunk; -import org.apache.tomcat.util.buf.MessageBytes; -import org.apache.tomcat.util.buf.UriNormalizer; -import org.apache.tomcat.util.http.Cookies; -import org.apache.tomcat.util.http.FastHttpDateFormat; -import org.apache.tomcat.util.http.HttpRequest; -import org.apache.tomcat.util.http.Parameters; -import org.apache.tomcat.util.http.ServerCookie; -import org.apache.tomcat.util.http.mapper.MappingData; - - -/** - * - * Wrapper object for the Coyote request. - * - * @author Remy Maucherat - * @author Craig R. McClanahan - */ -public class ServletRequestImpl implements HttpServletRequest { - - /** - * The request attribute under which we store the array of X509Certificate - * objects representing the certificate chain presented by our client, - * if any. - */ - public static final String CERTIFICATES_ATTR = - "javax.servlet.request.X509Certificate"; - - /** - * The request attribute under which we store the name of the cipher suite - * being used on an SSL connection (as an object of type - * java.lang.String). - */ - public static final String CIPHER_SUITE_ATTR = - "javax.servlet.request.cipher_suite"; - - /** - * Request dispatcher state. - */ - public static final String DISPATCHER_TYPE_ATTR = - "org.apache.catalina.core.DISPATCHER_TYPE"; - - /** - * Request dispatcher path. - */ - public static final String DISPATCHER_REQUEST_PATH_ATTR = - "org.apache.catalina.core.DISPATCHER_REQUEST_PATH"; - - /** - * The servlet context attribute under which we store the class path - * for our application class loader (as an object of type String), - * delimited with the appropriate path delimiter for this platform. - */ - public static final String CLASS_PATH_ATTR = - "org.apache.catalina.jsp_classpath"; - - - /** - * The request attribute under which we forward a Java exception - * (as an object of type Throwable) to an error page. - */ - public static final String EXCEPTION_ATTR = - "javax.servlet.error.exception"; - - - /** - * The request attribute under which we forward the request URI - * (as an object of type String) of the page on which an error occurred. - */ - public static final String EXCEPTION_PAGE_ATTR = - "javax.servlet.error.request_uri"; - - - /** - * The request attribute under which we forward a Java exception type - * (as an object of type Class) to an error page. - */ - public static final String EXCEPTION_TYPE_ATTR = - "javax.servlet.error.exception_type"; - - - /** - * The request attribute under which we forward an HTTP status message - * (as an object of type STring) to an error page. - */ - public static final String ERROR_MESSAGE_ATTR = - "javax.servlet.error.message"; - - - /** - * The request attribute under which we expose the value of the - * <jsp-file> value associated with this servlet, - * if any. - */ - public static final String JSP_FILE_ATTR = - "org.apache.catalina.jsp_file"; - - - /** - * The request attribute under which we store the key size being used for - * this SSL connection (as an object of type java.lang.Integer). - */ - public static final String KEY_SIZE_ATTR = - "javax.servlet.request.key_size"; - - /** - * The request attribute under which we store the session id being used - * for this SSL connection (as an object of type java.lang.String). - */ - public static final String SSL_SESSION_ID_ATTR = - "javax.servlet.request.ssl_session"; - - /** - * The request attribute under which we forward a servlet name to - * an error page. - */ - public static final String SERVLET_NAME_ATTR = - "javax.servlet.error.servlet_name"; - - - /** - * The name of the cookie used to pass the session identifier back - * and forth with the client. - */ - public static final String SESSION_COOKIE_NAME = "JSESSIONID"; - - - /** - * The name of the path parameter used to pass the session identifier - * back and forth with the client. - */ - public static final String SESSION_PARAMETER_NAME = "jsessionid"; - - - /** - * The request attribute under which we forward an HTTP status code - * (as an object of type Integer) to an error page. - */ - public static final String STATUS_CODE_ATTR = - "javax.servlet.error.status_code"; - - - /** - * The subject under which the AccessControlContext is running. - */ - public static final String SUBJECT_ATTR = - "javax.security.auth.subject"; - - - /** - * The servlet context attribute under which we store a temporary - * working directory (as an object of type File) for use by servlets - * within this web application. - */ - public static final String WORK_DIR_ATTR = - "javax.servlet.context.tempdir"; - - protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT"); - - - /** - * The default Locale if none are specified. - */ - protected static Locale defaultLocale = Locale.getDefault(); - - // ApplicationFilterFactory. What's the use ??? - private static Integer REQUEST_INTEGER = new Integer(8); - - /** - * The match string for identifying a session ID parameter. - */ - private static final String match = ";" + SESSION_PARAMETER_NAME + "="; - - /** - * The set of cookies associated with this Request. - */ - protected Cookie[] cookies = null; - - - /** - * The set of SimpleDateFormat formats to use in getDateHeader(). - * - * Notice that because SimpleDateFormat is not thread-safe, we can't - * declare formats[] as a static variable. - */ - protected SimpleDateFormat formats[] = null; - - - /** - * The attributes associated with this Request, keyed by attribute name. - */ - protected HashMap attributes = new HashMap(); - - - /** - * List of read only attributes for this Request. - */ - //private HashMap readOnlyAttributes = new HashMap(); - - - /** - * The preferred Locales assocaited with this Request. - */ - protected ArrayList locales = new ArrayList(); - - - /** - * Authentication type. - */ - protected String authType = null; - - /** - * User principal. - */ - protected Principal userPrincipal = null; - - - /** - * The Subject associated with the current AccessControllerContext - */ - protected transient Subject subject = null; - - - /** - * The current dispatcher type. - */ - protected Object dispatcherType = null; - - - /** - * The associated input buffer. - */ - protected BodyReader inputBuffer; - - Connector connector; - - /** - * ServletInputStream. - */ - protected ServletInputStreamImpl inputStream; - - - /** - * Reader. - */ - protected BufferedReader reader; - - - /** - * Using stream flag. - */ - protected boolean usingInputStream = false; - - - /** - * Using writer flag. - */ - protected boolean usingReader = false; - - - /** - * Session parsed flag. - */ - protected boolean sessionParsed = false; - - - /** - * Request parameters parsed flag. - */ - protected boolean parametersParsed = false; - - - /** - * Cookies parsed flag. - */ - protected boolean cookiesParsed = false; - - - /** - * Secure flag. - */ - protected boolean secure = false; - - - /** - * Hash map used in the getParametersMap method. - */ - protected ParameterMap parameterMap = new ParameterMap(); - - - /** - * The currently active session for this request. - */ - protected HttpSession session = null; - - - /** - * The current request dispatcher path. - */ - protected Object requestDispatcherPath = null; - - - /** - * Was the requested session ID received in a cookie? - */ - protected boolean requestedSessionCookie = false; - - - /** - * The requested session ID (if any) for this request. - */ - protected String requestedSessionId = null; - - - /** - * Was the requested session ID received in a URL? - */ - protected boolean requestedSessionURL = false; - - - /** - * Parse locales. - */ - protected boolean localesParsed = false; - - - /** - * Associated context. - */ - protected ServletContextImpl context = null; - - - - // --------------------------------------------------------- Public Methods - - /** - * Filter chain associated with the request. - */ - protected FilterChainImpl filterChain = new FilterChainImpl(); - - /** - * Mapping data. - */ - protected MappingData mappingData = new MappingData(); - - - // -------------------------------------------------------- Request Methods - - /** - * The response with which this request is associated. - */ - protected ServletResponseImpl response = new ServletResponseImpl(); - - /** - * URI byte to char converter (not recycled). - */ - // protected B2CConverter URIConverter = null; - - /** - * Associated wrapper. - */ - protected ServletConfigImpl wrapper = null; - - /** - * Post data buffer. - */ - public final static int CACHED_POST_LEN = 8192; - - public byte[] postData = null; - - - private HttpRequest httpRequest; - - /** New IO/buffer model - */ - //protected Http11Connection con; - - - public ServletRequestImpl() { - response.setRequest(this); - } - - -// /** -// * Return the Host within which this Request is being processed. -// */ -// public Host getHost() { -// if (getContext() == null) -// return null; -// return (Host) getContext().getParent(); -// //return ((Host) mappingData.host); -// } -// -// -// /** -// * Set the Host within which this Request is being processed. This -// * must be called as soon as the appropriate Host is identified, and -// * before the Request is passed to a context. -// * -// * @param host The newly associated Host -// */ -// public void setHost(Host host) { -// mappingData.host = host; -// } - - /** - * Add a Cookie to the set of Cookies associated with this Request. - * - * @param cookie The new cookie - */ - public void addCookie(Cookie cookie) { - - if (!cookiesParsed) - parseCookies(); - - int size = 0; - if (cookies != null) { - size = cookies.length; - } - - Cookie[] newCookies = new Cookie[size + 1]; - for (int i = 0; i < size; i++) { - newCookies[i] = cookies[i]; - } - newCookies[size] = cookie; - - cookies = newCookies; - - } - - /** - * Add a Header to the set of Headers associated with this Request. - * - * @param name The new header name - * @param value The new header value - */ - public void addHeader(String name, String value) { - // Not used - } - - public void setConnector(Connector c) { - connector = c; - } - - public Connector getConnector() { - return connector; - } - - /** - * Add a Locale to the set of preferred Locales for this Request. The - * first added Locale will be the first one returned by getLocales(). - * - * @param locale The new preferred Locale - */ - public void addLocale(Locale locale) { - locales.add(locale); - } - - - /** - * Add a parameter name and corresponding set of values to this Request. - * (This is used when restoring the original request on a form based - * login). - * - * @param name Name of this request parameter - * @param values Corresponding values for this request parameter - */ - public void addParameter(String name, String values[]) { - httpRequest.getParameters().addParameterValues(name, values); - } - - /** - * Clear the collection of Cookies associated with this Request. - */ - public void clearCookies() { - cookiesParsed = true; - cookies = null; - } - - /** - * Clear the collection of Headers associated with this Request. - */ - public void clearHeaders() { - // Not used - } - - /** - * Clear the collection of Locales associated with this Request. - */ - public void clearLocales() { - locales.clear(); - } - - /** - * Clear the collection of parameters associated with this Request. - */ - public void clearParameters() { - // Not used - } - - - /** - * Create and return a ServletInputStream to read the content - * associated with this Request. - * - * @exception IOException if an input/output error occurs - */ - public ServletInputStream createInputStream() - throws IOException { - return inputStream; - } - - /** - * Perform whatever actions are required to flush and close the input - * stream or reader, in a single operation. - * - * @exception IOException if an input/output error occurs - */ - public void finishRequest() throws IOException { - // The reader and input stream don't need to be closed - } - - - /** - * Return the specified request attribute if it exists; otherwise, return - * null. - * - * @param name Name of the request attribute to return - */ - public Object getAttribute(String name) { - - if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { - return (dispatcherType == null) - ? REQUEST_INTEGER - : dispatcherType; - } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { - return (requestDispatcherPath == null) - ? getRequestPathMB().toString() - : requestDispatcherPath.toString(); - } - - Object attr=attributes.get(name); - - if(attr!=null) - return(attr); - -// attr = reqB.getAttribute(name); -// if(attr != null) -// return attr; -// if( isSSLAttribute(name) ) { -// reqB.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE, -// reqB); -// attr = reqB.getAttribute(ServletRequestImpl.CERTIFICATES_ATTR); -// if( attr != null) { -// attributes.put(ServletRequestImpl.CERTIFICATES_ATTR, attr); -// } -// attr = reqB.getAttribute(ServletRequestImpl.CIPHER_SUITE_ATTR); -// if(attr != null) { -// attributes.put(ServletRequestImpl.CIPHER_SUITE_ATTR, attr); -// } -// attr = reqB.getAttribute(ServletRequestImpl.KEY_SIZE_ATTR); -// if(attr != null) { -// attributes.put(ServletRequestImpl.KEY_SIZE_ATTR, attr); -// } -// attr = reqB.getAttribute(ServletRequestImpl.SSL_SESSION_ID_ATTR); -// if(attr != null) { -// attributes.put(ServletRequestImpl.SSL_SESSION_ID_ATTR, attr); -// } -// attr = attributes.get(name); -// } - return attr; - } - - /** - * Return the names of all request attributes for this Request, or an - * empty Enumeration if there are none. - */ - public Enumeration getAttributeNames() { - if (isSecure()) { - getAttribute(ServletRequestImpl.CERTIFICATES_ATTR); - } - return new Enumerator(attributes.keySet(), true); - } - - - /** - * Return the authentication type used for this Request. - */ - public String getAuthType() { - return (authType); - } - - - // ------------------------------------------------- Request Public Methods - - - /** - * Return the character encoding for this Request. - */ - public String getCharacterEncoding() { - return (httpRequest.getCharacterEncoding()); - } - - - /** - * Return the content length for this Request. - */ - public int getContentLength() { - return (httpRequest.getContentLength()); - } - - -// /** -// * Return the object bound with the specified name to the internal notes -// * for this request, or null if no such binding exists. -// * -// * @param name Name of the note to be returned -// */ -// public Object getNote(String name) { -// return (notes.get(name)); -// } -// -// -// /** -// * Return an Iterator containing the String names of all notes bindings -// * that exist for this request. -// */ -// public Iterator getNoteNames() { -// return (notes.keySet().iterator()); -// } -// -// -// /** -// * Remove any object bound to the specified name in the internal notes -// * for this request. -// * -// * @param name Name of the note to be removed -// */ -// public void removeNote(String name) { -// notes.remove(name); -// } -// -// -// /** -// * Bind an object to a specified name in the internal notes associated -// * with this request, replacing any existing binding for this name. -// * -// * @param name Name to which the object should be bound -// * @param value Object to be bound to the specified name -// */ -// public void setNote(String name, Object value) { -// notes.put(name, value); -// } -// - - /** - * Return the content type for this Request. - */ - public String getContentType() { - return (httpRequest.getContentType()); - } - - - /** - * Return the Context within which this Request is being processed. - */ - public ServletContextImpl getContext() { - return (this.context); - } - - - /** - * Return the portion of the request URI used to select the Context - * of the Request. - */ - public String getContextPath() { - return (mappingData.contextPath.toString()); - } - - - /** - * Get the context path. - * - * @return the context path - */ - public MessageBytes getContextPathMB() { - return (mappingData.contextPath); - } - - - /** - * Return the set of Cookies received with this Request. - */ - public Cookie[] getCookies() { - - if (!cookiesParsed) - parseCookies(); - - return cookies; - - } - - - /** - * Return the value of the specified date header, if any; otherwise - * return -1. - * - * @param name Name of the requested date header - * - * @exception IllegalArgumentException if the specified header value - * cannot be converted to a date - */ - public long getDateHeader(String name) { - - String value = getHeader(name); - if (value == null) - return (-1L); - if (formats == null) { - formats = new SimpleDateFormat[] { - new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), - new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), - new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) - }; - formats[0].setTimeZone(GMT_ZONE); - formats[1].setTimeZone(GMT_ZONE); - formats[2].setTimeZone(GMT_ZONE); - } - - // Attempt to convert the date header in a variety of formats - long result = FastHttpDateFormat.parseDate(value, formats); - if (result != (-1L)) { - return result; - } - throw new IllegalArgumentException(value); - - } - - - /** - * Get the decoded request URI. - * - * @return the URL decoded request URI - */ - public String getDecodedRequestURI() { - return (httpRequest.decodedURI().toString()); - } - - - /** - * Get the decoded request URI. - * - * @return the URL decoded request URI - */ - public MessageBytes getDecodedRequestURIMB() { - return (httpRequest.decodedURI()); - } - - - /** - * Get filter chain associated with the request. - */ - public FilterChainImpl getFilterChain() { - return (this.filterChain); - } - - - // ------------------------------------------------- ServletRequest Methods - - /** - * Return the first value of the specified header, if any; otherwise, - * return null - * - * @param name Name of the requested header - */ - public String getHeader(String name) { - return httpRequest.getHeader(name); - } - - /** - * Return the names of all headers received with this request. - */ - public Enumeration getHeaderNames() { - return httpRequest.getMimeHeaders().names(); - } - - - /** - * Return all of the values of the specified header, if any; otherwise, - * return an empty enumeration. - * - * @param name Name of the requested header - */ - public Enumeration getHeaders(String name) { - return httpRequest.getMimeHeaders().values(name); - } - - /** - * Return the servlet input stream for this Request. The default - * implementation returns a servlet input stream created by - * createInputStream(). - * - * @exception IllegalStateException if getReader() has - * already been called for this request - * @exception IOException if an input/output error occurs - */ - public ServletInputStream getInputStream() throws IOException { - - if (usingReader) - throw new IllegalStateException - ("usingReader"); - - usingInputStream = true; - return inputStream; - - } - - - /** - * Return the value of the specified header as an integer, or -1 if there - * is no such header for this request. - * - * @param name Name of the requested header - * - * @exception IllegalArgumentException if the specified header value - * cannot be converted to an integer - */ - public int getIntHeader(String name) { - - String value = getHeader(name); - if (value == null) { - return (-1); - } else { - return (Integer.parseInt(value)); - } - - } - - - /** - * Returns the Internet Protocol (IP) address of the interface on - * which the request was received. - */ - public String getLocalAddr(){ - return httpRequest.localAddr().toString(); - } - - - /** - * Return the preferred Locale that the client will accept content in, - * based on the value for the first Accept-Language header - * that was encountered. If the request did not specify a preferred - * language, the server's default Locale is returned. - */ - public Locale getLocale() { - - if (!localesParsed) - parseLocales(); - - if (locales.size() > 0) { - return ((Locale) locales.get(0)); - } else { - return (defaultLocale); - } - - } - - - /** - * Return the set of preferred Locales that the client will accept - * content in, based on the values for any Accept-Language - * headers that were encountered. If the request did not specify a - * preferred language, the server's default Locale is returned. - */ - public Enumeration getLocales() { - - if (!localesParsed) - parseLocales(); - - if (locales.size() > 0) - return (new Enumerator(locales)); - ArrayList results = new ArrayList(); - results.add(defaultLocale); - return (new Enumerator(results)); - - } - - - /** - * Returns the host name of the Internet Protocol (IP) interface on - * which the request was received. - */ - public String getLocalName(){ - return httpRequest.localName().toString(); - } - - - /** - * Returns the Internet Protocol (IP) port number of the interface - * on which the request was received. - */ - public int getLocalPort(){ - return httpRequest.getLocalPort(); - } - - - /** - * Return mapping data. - */ - public MappingData getMappingData() { - return (mappingData); - } - - - - /** - * Return the HTTP request method used in this Request. - */ - public String getMethod() { - return httpRequest.method().toString(); - } - - - /** - * Return the value of the specified request parameter, if any; otherwise, - * return null. If there is more than one value defined, - * return only the first one. - * - * @param name Name of the desired request parameter - */ - public String getParameter(String name) { - - if (!parametersParsed) - parseParameters(); - - return httpRequest.getParameters().getParameter(name); - - } - - - /** - * Returns a Map of the parameters of this request. - * Request parameters are extra information sent with the request. - * For HTTP servlets, parameters are contained in the query string - * or posted form data. - * - * @return A Map containing parameter names as keys - * and parameter values as map values. - */ - public Map getParameterMap() { - - if (parameterMap.isLocked()) - return parameterMap; - - Enumeration enumeration = getParameterNames(); - while (enumeration.hasMoreElements()) { - String name = enumeration.nextElement().toString(); - String[] values = getParameterValues(name); - parameterMap.put(name, values); - } - - parameterMap.setLocked(true); - - return parameterMap; - - } - - - /** - * Return the names of all defined request parameters for this request. - */ - public Enumeration getParameterNames() { - - if (!parametersParsed) - parseParameters(); - - return httpRequest.getParameters().getParameterNames(); - - } - - - /** - * Return the defined values for the specified request parameter, if any; - * otherwise, return null. - * - * @param name Name of the desired request parameter - */ - public String[] getParameterValues(String name) { - - if (!parametersParsed) - parseParameters(); - - return httpRequest.getParameters().getParameterValues(name); - - } - - - /** - * Return the path information associated with this Request. - */ - public String getPathInfo() { - return (mappingData.pathInfo.toString()); - } - - - /** - * Get the path info. - * - * @return the path info - */ - public MessageBytes getPathInfoMB() { - return (mappingData.pathInfo); - } - - - /** - * Return the extra path information for this request, translated - * to a real path. - */ - public String getPathTranslated() { - - if (context == null) - return (null); - - if (getPathInfo() == null) { - return (null); - } else { - return (context.getServletContext().getRealPath(getPathInfo())); - } - - } - - /** - * Return the principal that has been authenticated for this Request. - */ - public Principal getPrincipal() { - return (userPrincipal); - } - - /** - * Return the protocol and version used to make this Request. - */ - public String getProtocol() { - return httpRequest.protocol().toString(); - } - - /** - * Return the query string associated with this request. - */ - public String getQueryString() { - String queryString = httpRequest.queryString().toString(); - if (queryString == null || queryString.equals("")) { - return (null); - } else { - return queryString; - } - } - - - /** - * Read the Reader wrapping the input stream for this Request. The - * default implementation wraps a BufferedReader around the - * servlet input stream returned by createInputStream(). - * - * @exception IllegalStateException if getInputStream() - * has already been called for this request - * @exception IOException if an input/output error occurs - */ - public BufferedReader getReader() throws IOException { - - if (usingInputStream) - throw new IllegalStateException - ("usingInputStream"); - - usingReader = true; - //inputBuffer.setConverter();// getB2C()); - return reader; - - } - - /** - * Cached list of encoders. - */ - protected HashMap encoders = new HashMap(); - - public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1"; - - /** - * Converter for the encoding associated with the request. - * If encoding is changed - a different encoder will be returned. - * - * Encoders are cached ( per request ) - at least 8K per charset - */ - public B2CConverter getB2C() throws IOException { - String enc = getCharacterEncoding(); - if (enc == null) { - enc = DEFAULT_CHARACTER_ENCODING; - } - B2CConverter conv = - (B2CConverter) encoders.get(enc); - if (conv == null) { - conv = new B2CConverter(enc); - encoders.put(enc, conv); - } - return conv; - } - - /** - * Return the real path of the specified virtual path. - * - * @param path Path to be translated - * - * @deprecated As of version 2.1 of the Java Servlet API, use - * ServletContext.getRealPath(). - */ - public String getRealPath(String path) { - - if (context == null) - return (null); - ServletContext servletContext = context; // .getServletContext(); - if (servletContext == null) - return (null); - else { - try { - return (servletContext.getRealPath(path)); - } catch (IllegalArgumentException e) { - return (null); - } - } - - } - - - /** - * Return the remote IP address making this Request. - */ - public String getRemoteAddr() { - if (httpRequest.remoteAddr().isNull()) { - httpRequest.remoteAddr().setString(connector.getRemoteAddr(this)); - } - return httpRequest.remoteAddr().toString(); - } - - - /** - * Return the remote host name making this Request. - */ - public String getRemoteHost() { - if (httpRequest.remoteHost().isNull()) { - httpRequest.remoteHost().setString(connector.getRemoteHost(this)); - } - return httpRequest.remoteHost().toString(); - } - - - /** - * Returns the Internet Protocol (IP) source port of the client - * or last proxy that sent the request. - */ - public int getRemotePort(){ - return httpRequest.getRemotePort(); - } - - - /** - * Return the name of the remote user that has been authenticated - * for this Request. - */ - public String getRemoteUser() { - - if (userPrincipal != null) { - return (userPrincipal.getName()); - } else { - return (null); - } - - } - - - /** - * Return the ServletRequest for which this object - * is the facade. This method must be implemented by a subclass. - */ - public HttpServletRequest getRequest() { - return this; - } - - public HttpRequest getHttpRequest() { - return httpRequest; - } - - public void setHttpRequest(HttpRequest req, BodyReader in) { - this.httpRequest = req; - inputBuffer = in; - inputStream = new ServletInputStreamImpl(inputBuffer.asInputStream()); - reader = new ServletReaderImpl(inputBuffer); - } - - public BodyReader getBodyReader() { - return inputBuffer; - } - - /** - * Return a RequestDispatcher that wraps the resource at the specified - * path, which may be interpreted as relative to the current request path. - * - * @param path Path of the resource to be wrapped - */ - public RequestDispatcher getRequestDispatcher(String path) { - - if (context == null) - return (null); - - // If the path is already context-relative, just pass it through - if (path == null) - return (null); - else if (path.startsWith("/")) - return (context.getRequestDispatcher(path)); - - // Convert a request-relative path to a context-relative one - String servletPath = (String) getAttribute(RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR); - if (servletPath == null) - servletPath = getServletPath(); - - // Add the path info, if there is any - String pathInfo = getPathInfo(); - String requestPath = null; - - if (pathInfo == null) { - requestPath = servletPath; - } else { - requestPath = servletPath + pathInfo; - } - - int pos = requestPath.lastIndexOf('/'); - String relative = null; - if (pos >= 0) { - relative = RequestUtil.normalize - (requestPath.substring(0, pos + 1) + path); - } else { - relative = RequestUtil.normalize(requestPath + path); - } - - return (context.getRequestDispatcher(relative)); - - } - - - /** - * Return the session identifier included in this request, if any. - */ - public String getRequestedSessionId() { - return (requestedSessionId); - } - - - // ---------------------------------------------------- HttpRequest Methods - - - /** - * Get the request path. - * - * @return the request path - */ - public MessageBytes getRequestPathMB() { - return (mappingData.requestPath); - } - - - /** - * Return the request URI for this request. - */ - public String getRequestURI() { - return httpRequest.requestURI().toString(); - } - - /** - */ - public void setRequestURI(String uri) { - httpRequest.decodedURI().setString(uri); - try { - UriNormalizer.decodeRequest(httpRequest.decodedURI(), - httpRequest.requestURI(), httpRequest.getURLDecoder()); - } catch(IOException ioe) { - ioe.printStackTrace(); - return; - } - } - - - - /** - * Reconstructs the URL the client used to make the request. - * The returned URL contains a protocol, server name, port - * number, and server path, but it does not include query - * string parameters. - *

- * Because this method returns a StringBuffer, - * not a String, you can modify the URL easily, - * for example, to append query parameters. - *

- * This method is useful for creating redirect messages and - * for reporting errors. - * - * @return A StringBuffer object containing the - * reconstructed URL - */ - public StringBuffer getRequestURL() { - - StringBuffer url = new StringBuffer(); - String scheme = getScheme(); - int port = getServerPort(); - if (port < 0) - port = 80; // Work around java.net.URL bug - - url.append(scheme); - url.append("://"); - url.append(getServerName()); - if ((scheme.equals("http") && (port != 80)) - || (scheme.equals("https") && (port != 443))) { - url.append(':'); - url.append(port); - } - url.append(getRequestURI()); - - return (url); - - } - - - /** - * Return the Response with which this Request is associated. - */ - public ServletResponseImpl getResponse() { - return (this.response); - } - - - /** - * Return the scheme used to make this Request. - */ - public String getScheme() { - String scheme = httpRequest.scheme().toString(); - if (scheme == null) { - scheme = (isSecure() ? "https" : "http"); - } - return scheme; - } - - - /** - * Return the server name responding to this Request. - */ - public String getServerName() { - return (httpRequest.serverName().toString()); - } - - - /** - * Return the server port responding to this Request. - */ - public int getServerPort() { - return (httpRequest.getServerPort()); - } - - - /** - * Return the portion of the request URI used to select the servlet - * that will process this request. - */ - public String getServletPath() { - return (mappingData.wrapperPath.toString()); - } - - - /** - * Get the servlet path. - * - * @return the servlet path - */ - public MessageBytes getServletPathMB() { - return (mappingData.wrapperPath); - } - - - - /** - * Return the input stream associated with this Request. - */ - public InputStream getStream() { - return inputStream; - } - - - /** - * Return the principal that has been authenticated for this Request. - */ - public Principal getUserPrincipal() { - return userPrincipal; - } - - - /** - * Return the Wrapper within which this Request is being processed. - */ - public ServletConfigImpl getWrapper() { - return (this.wrapper); - } - - - /** - * Return true if the session identifier included in this - * request came from a cookie. - */ - public boolean isRequestedSessionIdFromCookie() { - - if (requestedSessionId != null) - return (requestedSessionCookie); - else - return (false); - - } - - - /** - * Return true if the session identifier included in this - * request came from the request URI. - * - * @deprecated As of Version 2.1 of the Java Servlet API, use - * isRequestedSessionIdFromURL() instead. - */ - public boolean isRequestedSessionIdFromUrl() { - return (isRequestedSessionIdFromURL()); - } - - - /** - * Return true if the session identifier included in this - * request came from the request URI. - */ - public boolean isRequestedSessionIdFromURL() { - - if (requestedSessionId != null) - return (requestedSessionURL); - else - return (false); - - } - - - /** - * Return true if the session identifier included in this - * request identifies a valid session. - */ - public boolean isRequestedSessionIdValid() { - - if (requestedSessionId == null) - return (false); - if (context == null) - return (false); - UserSessionManager manager = context.getManager(); - if (manager == null) - return (false); - HttpSession session = null; - try { - session = manager.findSession(requestedSessionId); - } catch (IOException e) { - session = null; - } - if ((session != null) && manager.isValid(session)) - return (true); - else - return (false); - - } - - /** - * Was this request received on a secure connection? - */ - public boolean isSecure() { - return (secure); - } - - - /** - * Return true if the authenticated user principal - * possesses the specified role name. - * - * @param role Role name to be validated - */ - public boolean isUserInRole(String role) { - // Have we got an authenticated principal at all? - Principal userPrincipal = getPrincipal(); - if (userPrincipal == null) - return (false); - - // Identify the Realm we will use for checking role assignmenets - if (context == null) - return (false); - - // Check for a role alias defined in a element - if (wrapper != null) { - String realRole = wrapper.getSecurityRoleRef(role); - if (realRole != null) { - role = realRole; - } - } - - if (role.equals(userPrincipal.getName())) { - return true; - } - - // TODO: check !!!! - // Check for a role defined directly as a - return false; - } - - /** - * Release all object references, and initialize instance variables, in - * preparation for reuse of this object. - */ - public void recycle() { - - wrapper = null; - - dispatcherType = null; - requestDispatcherPath = null; - - authType = null; - inputBuffer.recycle(); - usingInputStream = false; - usingReader = false; - userPrincipal = null; - subject = null; - sessionParsed = false; - parametersParsed = false; - cookiesParsed = false; - locales.clear(); - localesParsed = false; - secure = false; - - attributes.clear(); - //notes.clear(); - cookies = null; - - if (session != null) { - context.getManager().endAccess(session); - } - context = null; - session = null; - requestedSessionCookie = false; - requestedSessionId = null; - requestedSessionURL = false; - - parameterMap.setLocked(false); - parameterMap.clear(); - - mappingData.recycle(); - httpRequest.recycle(); - } - - - /** - * Remove the specified request attribute if it exists. - * - * @param name Name of the request attribute to remove - */ - public void removeAttribute(String name) { - Object value = null; - boolean found = false; - - // Remove the specified attribute - // Check for read only attribute - // requests are per thread so synchronization unnecessary -// if (readOnlyAttributes.containsKey(name)) { -// return; -// } - found = attributes.containsKey(name); - if (found) { - value = attributes.get(name); - attributes.remove(name); - } else { - return; - } - - // Notify interested application event listeners - List listeners = context.getListeners(); - if (listeners.size() == 0) - return; - ServletRequestAttributeEvent event = null; - for (int i = 0; i < listeners.size(); i++) { - if (!(listeners.get(i) instanceof ServletRequestAttributeListener)) - continue; - ServletRequestAttributeListener listener = - (ServletRequestAttributeListener) listeners.get(i); - try { - if (event == null) { - event = - new ServletRequestAttributeEvent(context.getServletContext(), - getRequest(), name, value); - } - listener.attributeRemoved(event); - } catch (Throwable t) { - context.getLogger().log(Level.WARNING, "ServletRequestAttributeListner.attributeRemoved()", t); - // Error valve will pick this execption up and display it to user - attributes.put( ServletRequestImpl.EXCEPTION_ATTR, t ); - } - } - } - - - /** - * Set the specified request attribute to the specified value. - * - * @param name Name of the request attribute to set - * @param value The associated value - */ - public void setAttribute(String name, Object value) { - - // Name cannot be null - if (name == null) - throw new IllegalArgumentException - ("setAttribute() name == null"); - - // Null value is the same as removeAttribute() - if (value == null) { - removeAttribute(name); - return; - } - - if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { - dispatcherType = value; - return; - } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { - requestDispatcherPath = value; - return; - } - - Object oldValue = null; - boolean replaced = false; - - // Add or replace the specified attribute - // Check for read only attribute - // requests are per thread so synchronization unnecessary -// if (readOnlyAttributes.containsKey(name)) { -// return; -// } - - oldValue = attributes.put(name, value); - if (oldValue != null) { - replaced = true; - } - - // Pass special attributes to the native layer -// if (name.startsWith("org.apache.tomcat.")) { -// reqB.setAttribute(name, value); -// } -// - // Notify interested application event listeners - List listeners = context.getListeners(); - if (listeners.size() == 0) - return; - ServletRequestAttributeEvent event = null; - - for (int i = 0; i < listeners.size(); i++) { - if (!(listeners.get(i) instanceof ServletRequestAttributeListener)) - continue; - ServletRequestAttributeListener listener = - (ServletRequestAttributeListener) listeners.get(i); - try { - if (event == null) { - if (replaced) - event = - new ServletRequestAttributeEvent(context.getServletContext(), - getRequest(), name, oldValue); - else - event = - new ServletRequestAttributeEvent(context.getServletContext(), - getRequest(), name, value); - } - if (replaced) { - listener.attributeReplaced(event); - } else { - listener.attributeAdded(event); - } - } catch (Throwable t) { - context.getLogger().log(Level.WARNING, "ServletRequestAttributeListener error", t); - // Error valve will pick this execption up and display it to user - attributes.put( ServletRequestImpl.EXCEPTION_ATTR, t ); - } - } - } - - - // --------------------------------------------- HttpServletRequest Methods - - - /** - * Set the authentication type used for this request, if any; otherwise - * set the type to null. Typical values are "BASIC", - * "DIGEST", or "SSL". - * - * @param type The authentication type used - */ - public void setAuthType(String type) { - this.authType = type; - } - - - /** - * Overrides the name of the character encoding used in the body of - * this request. This method must be called prior to reading request - * parameters or reading input using getReader(). - * - * @param enc The character encoding to be used - * - * @exception UnsupportedEncodingException if the specified encoding - * is not supported - * - * @since Servlet 2.3 - */ - public void setCharacterEncoding(String enc) - throws UnsupportedEncodingException { - - // Ensure that the specified encoding is valid - byte buffer[] = new byte[1]; - buffer[0] = (byte) 'a'; - String dummy = new String(buffer, enc); - - // Save the validated encoding - httpRequest.setCharacterEncoding(enc); - - } - - -// public void setConnection(Http11Connection con) { -// this.con = con; -// //reqB.messageWriter.setConnection(con); -// //inputBuffer.setRequest(req); -// } - - - /** - * Set the content length associated with this Request. - * - * @param length The new content length - */ - public void setContentLength(int length) { - // Not used - } - - - /** - * Set the content type (and optionally the character encoding) - * associated with this Request. For example, - * text/html; charset=ISO-8859-4. - * - * @param type The new content type - */ - public void setContentType(String type) { - // Not used - } - - - /** - * Set the Context within which this Request is being processed. This - * must be called as soon as the appropriate Context is identified, because - * it identifies the value to be returned by getContextPath(), - * and thus enables parsing of the request URI. - * - * @param context The newly associated Context - */ - public void setContext(ServletContextImpl context) { - this.context = context; - } - - - /** - * Set the context path for this Request. This will normally be called - * when the associated Context is mapping the Request to a particular - * Wrapper. - * - * @param path The context path - */ - public void setContextPath(String path) { - - if (path == null) { - mappingData.contextPath.setString(""); - } else { - mappingData.contextPath.setString(path); - } - - } - - - /** - * Set the set of cookies recieved with this Request. - */ - public void setCookies(Cookie[] cookies) { - - this.cookies = cookies; - - } - - - /** - * Set the decoded request URI. - * - * @param uri The decoded request URI - */ - public void setDecodedRequestURI(String uri) { - // Not used - } - - - /** - * Set the HTTP request method used for this Request. - * - * @param method The request method - */ - public void setMethod(String method) { - httpRequest.method().setString(method); - } - - - /** - * Set the path information for this Request. This will normally be called - * when the associated Context is mapping the Request to a particular - * Wrapper. - * - * @param path The path information - */ - public void setPathInfo(String path) { - mappingData.pathInfo.setString(path); - } - - - /** - * Set the protocol name and version associated with this Request. - * - * @param protocol Protocol name and version - */ - public void setProtocol(String protocol) { - // Not used - } - - - /** - * Set the query string for this Request. This will normally be called - * by the HTTP Connector, when it parses the request headers. - * - * @param query The query string - */ - public void setQueryString(String query) { - // Not used - } - - - /** - * Set the IP address of the remote client associated with this Request. - * - * @param remoteAddr The remote IP address - */ - public void setRemoteAddr(String remoteAddr) { - // Not used - } - - - /** - * Set the fully qualified name of the remote client associated with this - * Request. - * - * @param remoteHost The remote host name - */ - public void setRemoteHost(String remoteHost) { - // Not used - } - - - /** - * Set a flag indicating whether or not the requested session ID for this - * request came in through a cookie. This is normally called by the - * HTTP Connector, when it parses the request headers. - * - * @param flag The new flag - */ - public void setRequestedSessionCookie(boolean flag) { - - this.requestedSessionCookie = flag; - - } - - - /** - * Set the requested session ID for this request. This is normally called - * by the HTTP Connector, when it parses the request headers. - * - * @param id The new session id - */ - public void setRequestedSessionId(String id) { - - this.requestedSessionId = id; - - } - - - /** - * Set a flag indicating whether or not the requested session ID for this - * request came in through a URL. This is normally called by the - * HTTP Connector, when it parses the request headers. - * - * @param flag The new flag - */ - public void setRequestedSessionURL(boolean flag) { - - this.requestedSessionURL = flag; - - } - - - /** - * Set the name of the scheme associated with this request. Typical values - * are http, https, and ftp. - * - * @param scheme The scheme - */ - public void setScheme(String scheme) { - // Not used - } - - - /** - * Set the value to be returned by isSecure() - * for this Request. - * - * @param secure The new isSecure value - */ - public void setSecure(boolean secure) { - this.secure = secure; - } - - - /** - * Set the name of the server (virtual host) to process this request. - * - * @param name The server name - */ - public void setServerName(String name) { - httpRequest.serverName().setString(name); - } - - - /** - * Set the port number of the server to process this request. - * - * @param port The server port - */ - public void setServerPort(int port) { - httpRequest.setServerPort(port); - } - - - /** - * Set the servlet path for this Request. This will normally be called - * when the associated Context is mapping the Request to a particular - * Wrapper. - * - * @param path The servlet path - */ - public void setServletPath(String path) { - if (path != null) - mappingData.wrapperPath.setString(path); - } - - - /** - * Set the input stream associated with this Request. - * - * @param stream The new input stream - */ - public void setStream(InputStream stream) { - // Ignore - } - - - /** - * Set the Principal who has been authenticated for this Request. This - * value is also used to calculate the value to be returned by the - * getRemoteUser() method. - * - * @param principal The user Principal - */ - public void setUserPrincipal(Principal principal) { - - if (System.getSecurityManager() != null){ - HttpSession session = getSession(false); - if ( (subject != null) && - (!subject.getPrincipals().contains(principal)) ){ - subject.getPrincipals().add(principal); - } else if (session != null && - session.getAttribute(ServletRequestImpl.SUBJECT_ATTR) == null) { - subject = new Subject(); - subject.getPrincipals().add(principal); - } - if (session != null){ - session.setAttribute(ServletRequestImpl.SUBJECT_ATTR, subject); - } - } - - this.userPrincipal = principal; - } - - - /** - * Set the Wrapper within which this Request is being processed. This - * must be called as soon as the appropriate Wrapper is identified, and - * before the Request is ultimately passed to an application servlet. - * @param wrapper The newly associated Wrapper - */ - public void setWrapper(ServletConfigImpl wrapper) { - this.wrapper = wrapper; - } - - - public String toString() { - return httpRequest.requestURI().toString(); - } - - - /** - * Configures the given JSESSIONID cookie. - * - * @param cookie The JSESSIONID cookie to be configured - */ - protected void configureSessionCookie(Cookie cookie) { - cookie.setMaxAge(-1); - String contextPath = null; - if (//!connector.getEmptySessionPath() && - (getContext() != null)) { - contextPath = getContext().getEncodedPath(); - } - if ((contextPath != null) && (contextPath.length() > 0)) { - cookie.setPath(contextPath); - } else { - cookie.setPath("/"); - } - if (isSecure()) { - cookie.setSecure(true); - } - } - - - /** - * Return the session associated with this Request, creating one - * if necessary. - */ - public HttpSession getSession() { - return getSession(true); - } - - - public HttpSession getSession(boolean create) { - - // There cannot be a session if no context has been assigned yet - if (context == null) - return (null); - - - // Return the requested session if it exists and is valid - UserSessionManager manager = null; - if (context != null) - manager = context.getManager(); - if (manager == null) - return (null); // Sessions are not supported - - // Return the current session if it exists and is valid - if ((session != null) && !manager.isValid(session)) - session = null; - if (session != null) - return (session); - - - if (requestedSessionId != null) { - try { - session = manager.findSession(requestedSessionId); - } catch (IOException e) { - session = null; - } - if ((session != null) && !manager.isValid(session)) - session = null; - if (session != null) { - manager.access(session); - return (session); - } - } - - // Create a new session if requested and the response is not committed - if (!create) - return (null); - if ((context != null) && (response != null) && - context.getCookies() && - getResponse().isCommitted()) { - throw new IllegalStateException - ("isCommited()"); - } - - // Attempt to reuse session id if one was submitted in a cookie - // Do not reuse the session id if it is from a URL, to prevent possible - // phishing attacks - if (// connector.getEmptySessionPath() && - isRequestedSessionIdFromCookie()) { - session = manager.createSession(getRequestedSessionId()); - } else { - session = manager.createSession(null); - } - - // Creating a new session cookie based on that session - if ((session != null) && (getContext() != null) - && getContext().getCookies()) { - Cookie cookie = new Cookie(ServletRequestImpl.SESSION_COOKIE_NAME, - session.getId()); - configureSessionCookie(cookie); - response.addCookie(cookie); - } - - if (session != null) { - manager.access(session); - return (session); - } else { - return (null); - } - - } - - - /** - * Return the URI converter. - */ -// protected B2CConverter getURIConverter() { -// return URIConverter; -// } -// - - // ------------------------------------------------------ Protected Methods - - - /** - * Parse cookies. - */ - protected void parseCookies() { - - cookiesParsed = true; - - Cookies serverCookies = httpRequest.getCookies(); - int count = serverCookies.getCookieCount(); - if (count <= 0) - return; - - cookies = new Cookie[count]; - - int idx=0; - for (int i = 0; i < count; i++) { - ServerCookie scookie = serverCookies.getCookie(i); - try { - Cookie cookie = new Cookie(scookie.getName().toString(), - scookie.getValue().toString()); - cookie.setPath(scookie.getPath().toString()); - cookie.setVersion(scookie.getVersion()); - String domain = scookie.getDomain().toString(); - if (domain != null) { - cookie.setDomain(scookie.getDomain().toString()); - } - cookies[idx++] = cookie; - } catch(IllegalArgumentException e) { - // Ignore bad cookie - } - } - if( idx < count ) { - Cookie [] ncookies = new Cookie[idx]; - System.arraycopy(cookies, 0, ncookies, 0, idx); - cookies = ncookies; - } - - } - - /** - * Parse request locales. - */ - protected void parseLocales() { - - localesParsed = true; - - Enumeration values = getHeaders("accept-language"); - - while (values.hasMoreElements()) { - String value = values.nextElement().toString(); - parseLocalesHeader(value); - } - - } - - /** - * Parse accept-language header value. - */ - protected void parseLocalesHeader(String value) { - - TreeMap locales = new LocaleParser().parseLocale(value); - // Process the quality values in highest->lowest order (due to - // negating the Double value when creating the key) - Iterator keys = locales.keySet().iterator(); - while (keys.hasNext()) { - Double key = (Double) keys.next(); - ArrayList list = (ArrayList) locales.get(key); - Iterator values = list.iterator(); - while (values.hasNext()) { - Locale locale = (Locale) values.next(); - addLocale(locale); - } - } - - } - - /** - * Parse request parameters. - */ - protected void parseParameters() { - - parametersParsed = true; - - Parameters parameters = httpRequest.getParameters(); - - // getCharacterEncoding() may have been overridden to search for - // hidden form field containing request encoding - String enc = getCharacterEncoding(); - -// boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); - if (enc != null) { - parameters.setEncoding(enc); -// if (useBodyEncodingForURI) { -// parameters.setQueryStringEncoding(enc); -// } - } else { - parameters.setEncoding(DEFAULT_CHARACTER_ENCODING); -// if (useBodyEncodingForURI) { -// parameters.setQueryStringEncoding -// (DEFAULT_CHARACTER_ENCODING); -// } - } - - parameters.handleQueryParameters(); - - if (usingInputStream || usingReader) - return; - - if (!getMethod().equalsIgnoreCase("POST")) - return; - - String contentType = getContentType(); - if (contentType == null) - contentType = ""; - int semicolon = contentType.indexOf(';'); - if (semicolon >= 0) { - contentType = contentType.substring(0, semicolon).trim(); - } else { - contentType = contentType.trim(); - } - if (!("application/x-www-form-urlencoded".equals(contentType))) - return; - - int len = getContentLength(); - - if (len > 0) { -// int maxPostSize = connector.getMaxPostSize(); -// if ((maxPostSize > 0) && (len > maxPostSize)) { -// context.getLogger().info -// (sm.getString("reqB.postTooLarge")); -// throw new IllegalStateException("Post too large"); -// } - try { - byte[] formData = null; - if (len < CACHED_POST_LEN) { - if (postData == null) - postData = new byte[CACHED_POST_LEN]; - formData = postData; - } else { - formData = new byte[len]; - } - int actualLen = readPostBody(formData, len); - if (actualLen == len) { - parameters.processParameters(formData, 0, len); - } - } catch (Throwable t) { - ; // Ignore - } - } - - } - - - /** - * Parse session id in URL. Done in request for performance. - * TODO: should be done in manager - */ - protected void parseSessionCookiesId() { - String sessionCookieName = context.getSessionCookieName(); - - // Parse session id from cookies - Cookies serverCookies = httpRequest.getCookies(); - int count = serverCookies.getCookieCount(); - if (count <= 0) - return; - - for (int i = 0; i < count; i++) { - ServerCookie scookie = serverCookies.getCookie(i); - if (scookie.getName().equals(sessionCookieName)) { - // Override anything requested in the URL - if (!isRequestedSessionIdFromCookie()) { - // Accept only the first session id cookie - //scookie.getValue().convertToAscii(); - - setRequestedSessionId - (scookie.getValue().toString()); - setRequestedSessionCookie(true); - setRequestedSessionURL(false); - } else { - if (!isRequestedSessionIdValid()) { - // Replace the session id until one is valid - //scookie.getValue().convertToAscii(); - setRequestedSessionId - (scookie.getValue().toString()); - } - } - } - } - } - - /** - * Parse session id in URL. - */ - protected void parseSessionId() { - ServletRequestImpl request = this; - ByteChunk uriBC = httpRequest.requestURI().getByteChunk(); - int semicolon = uriBC.indexOf(match, 0, match.length(), 0); - - if (semicolon > 0) { - - // Parse session ID, and extract it from the decoded request URI - int start = uriBC.getStart(); - int end = uriBC.getEnd(); - - int sessionIdStart = semicolon + match.length(); - int semicolon2 = uriBC.indexOf(';', sessionIdStart); - if (semicolon2 >= 0) { - request.setRequestedSessionId - (new String(uriBC.getBuffer(), start + sessionIdStart, - semicolon2 - sessionIdStart)); - // Extract session ID from request URI - byte[] buf = uriBC.getBuffer(); - for (int i = 0; i < end - start - semicolon2; i++) { - buf[start + semicolon + i] - = buf[start + i + semicolon2]; - } - uriBC.setBytes(buf, start, end - start - semicolon2 + semicolon); - } else { - request.setRequestedSessionId - (new String(uriBC.getBuffer(), start + sessionIdStart, - (end - start) - sessionIdStart)); - uriBC.setEnd(start + semicolon); - } - request.setRequestedSessionURL(true); - - } else { - request.setRequestedSessionId(null); - request.setRequestedSessionURL(false); - } - - } - - - /** - * Read post body in an array. - */ - protected int readPostBody(byte body[], int len) - throws IOException { - - int offset = 0; - do { - int inputLen = getStream().read(body, offset, len - offset); - if (inputLen <= 0) { - return offset; - } - offset += inputLen; - } while ((len - offset) > 0); - return len; - - } - - - /** - * Test if a given name is one of the special Servlet-spec SSL attributes. - */ - static boolean isSSLAttribute(String name) { - return ServletRequestImpl.CERTIFICATES_ATTR.equals(name) || - ServletRequestImpl.CIPHER_SUITE_ATTR.equals(name) || - ServletRequestImpl.KEY_SIZE_ATTR.equals(name) || - ServletRequestImpl.SSL_SESSION_ID_ATTR.equals(name); - } - - - public void addAsyncListener(AsyncListener listener) { - } - - - - public void addAsyncListener(AsyncListener listener, - ServletRequest servletRequest, - ServletResponse servletResponse) { - } - - - - @Override - public AsyncContext getAsyncContext() { - return null; - } - - - - @Override - public ServletContext getServletContext() { - return null; - } - - - - @Override - public boolean isAsyncStarted() { - return false; - } - - - - @Override - public boolean isAsyncSupported() { - return false; - } - - - public void setAsyncTimeout(long timeout) { - } - - - - @Override - public AsyncContext startAsync() throws IllegalStateException { - return null; - } - - - @Override - public AsyncContext startAsync(ServletRequest servletRequest, - ServletResponse servletResponse) - throws IllegalStateException { - return null; - } - - - - @Override - public boolean authenticate(HttpServletResponse response) - throws IOException, ServletException { - return false; - } - - - - @Override - public Part getPart(String name) { - return null; - } - - - - @Override - public void login(String username, String password) throws ServletException { - } - - - - @Override - public void logout() throws ServletException { - } - - - - public long getAsyncTimeout() { - return 0; - } - - - - @Override - public DispatcherType getDispatcherType() { - return null; - } - - - @Override - public Collection getParts() throws IOException, ServletException { - return null; - } - - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletRequestWrapperImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletRequestWrapperImpl.java deleted file mode 100644 index af400d8ca..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletRequestWrapperImpl.java +++ /dev/null @@ -1,943 +0,0 @@ -/* - * 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.tomcat.lite; - - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; - -import javax.servlet.RequestDispatcher; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpSession; - -import org.apache.tomcat.addons.UserSessionManager; -import org.apache.tomcat.servlets.util.Enumerator; -import org.apache.tomcat.servlets.util.RequestUtil; - - -/** - * Wrapper around a javax.servlet.http.HttpServletRequest - * that transforms an application request object (which might be the original - * one passed to a servlet, or might be based on the 2.3 - * javax.servlet.http.HttpServletRequestWrapper class) - * back into an internal org.apache.catalina.HttpRequest. - *

- * WARNING: Due to Java's lack of support for multiple - * inheritance, all of the logic in ApplicationRequest is - * duplicated in ApplicationHttpRequest. Make sure that you - * keep these two classes in synchronization when making changes! - * - * @author Craig R. McClanahan - * @author Remy Maucherat - */ -public class ServletRequestWrapperImpl extends HttpServletRequestWrapper { - - - // ------------------------------------------------------- Static Variables - - - /** - * The set of attribute names that are special for request dispatchers. - */ - protected static final String specials[] = - { RequestDispatcherImpl.INCLUDE_REQUEST_URI_ATTR, - RequestDispatcherImpl.INCLUDE_CONTEXT_PATH_ATTR, - RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR, - RequestDispatcherImpl.INCLUDE_PATH_INFO_ATTR, - RequestDispatcherImpl.INCLUDE_QUERY_STRING_ATTR, - RequestDispatcherImpl.FORWARD_REQUEST_URI_ATTR, - RequestDispatcherImpl.FORWARD_CONTEXT_PATH_ATTR, - RequestDispatcherImpl.FORWARD_SERVLET_PATH_ATTR, - RequestDispatcherImpl.FORWARD_PATH_INFO_ATTR, - RequestDispatcherImpl.FORWARD_QUERY_STRING_ATTR }; - - - // ----------------------------------------------------------- Constructors - - - /** - * Construct a new wrapped request around the specified servlet request. - * - * @param request The servlet request being wrapped - */ - public ServletRequestWrapperImpl(HttpServletRequest request, - ServletContextImpl context, - boolean crossContext) { - - super(request); - this.context = context; - this.crossContext = crossContext; - setRequest(request); - - } - - - // ----------------------------------------------------- Instance Variables - - - /** - * The context for this request. - */ - protected ServletContextImpl context = null; - - - /** - * The context path for this request. - */ - protected String contextPath = null; - - - /** - * If this request is cross context, since this changes session accesss - * behavior. - */ - protected boolean crossContext = false; - - - /** - * The current dispatcher type. - */ - protected Object dispatcherType = null; - - - /** - * Descriptive information about this implementation. - */ - protected static final String info = - "org.apache.catalina.core.ApplicationHttpRequest/1.0"; - - - /** - * The request parameters for this request. This is initialized from the - * wrapped request, but updates are allowed. - */ - protected Map parameters = null; - - - /** - * Have the parameters for this request already been parsed? - */ - private boolean parsedParams = false; - - - /** - * The path information for this request. - */ - protected String pathInfo = null; - - - /** - * The query parameters for the current request. - */ - private String queryParamString = null; - - - /** - * The query string for this request. - */ - protected String queryString = null; - - - /** - * The current request dispatcher path. - */ - protected Object requestDispatcherPath = null; - - - /** - * The request URI for this request. - */ - protected String requestURI = null; - - - /** - * The servlet path for this request. - */ - protected String servletPath = null; - - - /** - * The currently active session for this request. - */ - protected HttpSession session = null; - - - /** - * Special attributes. - */ - protected Object[] specialAttributes = new Object[specials.length]; - - - // ------------------------------------------------- ServletRequest Methods - - - /** - * Override the getAttribute() method of the wrapped request. - * - * @param name Name of the attribute to retrieve - */ - public Object getAttribute(String name) { - - if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { - return dispatcherType; - } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { - if ( requestDispatcherPath != null ){ - return requestDispatcherPath.toString(); - } else { - return null; - } - } - - int pos = getSpecial(name); - if (pos == -1) { - return getRequest().getAttribute(name); - } else { - if ((specialAttributes[pos] == null) - && (specialAttributes[5] == null) && (pos >= 5)) { - // If it's a forward special attribute, and null, it means this - // is an include, so we check the wrapped request since - // the request could have been forwarded before the include - return getRequest().getAttribute(name); - } else { - return specialAttributes[pos]; - } - } - - } - - - /** - * Override the getAttributeNames() method of the wrapped - * request. - */ - public Enumeration getAttributeNames() { - return (new AttributeNamesEnumerator()); - } - - - /** - * Override the removeAttribute() method of the - * wrapped request. - * - * @param name Name of the attribute to remove - */ - public void removeAttribute(String name) { - - if (!removeSpecial(name)) - getRequest().removeAttribute(name); - - } - - - /** - * Override the setAttribute() method of the - * wrapped request. - * - * @param name Name of the attribute to set - * @param value Value of the attribute to set - */ - public void setAttribute(String name, Object value) { - - if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) { - dispatcherType = value; - return; - } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) { - requestDispatcherPath = value; - return; - } - - if (!setSpecial(name, value)) { - getRequest().setAttribute(name, value); - } - - } - - - /** - * Return a RequestDispatcher that wraps the resource at the specified - * path, which may be interpreted as relative to the current request path. - * - * @param path Path of the resource to be wrapped - */ - public RequestDispatcher getRequestDispatcher(String path) { - - if (context == null) - return (null); - - // If the path is already context-relative, just pass it through - if (path == null) - return (null); - else if (path.startsWith("/")) - return (context.getServletContext().getRequestDispatcher(path)); - - // Convert a request-relative path to a context-relative one - String servletPath = - (String) getAttribute(RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR); - if (servletPath == null) - servletPath = getServletPath(); - - // Add the path info, if there is any - String pathInfo = getPathInfo(); - String requestPath = null; - - if (pathInfo == null) { - requestPath = servletPath; - } else { - requestPath = servletPath + pathInfo; - } - - int pos = requestPath.lastIndexOf('/'); - String relative = null; - if (pos >= 0) { - relative = RequestUtil.normalize - (requestPath.substring(0, pos + 1) + path); - } else { - relative = RequestUtil.normalize(requestPath + path); - } - - return (context.getServletContext().getRequestDispatcher(relative)); - - } - - - // --------------------------------------------- HttpServletRequest Methods - - - /** - * Override the getContextPath() method of the wrapped - * request. - */ - public String getContextPath() { - - return (this.contextPath); - - } - - - /** - * Override the getParameter() method of the wrapped request. - * - * @param name Name of the requested parameter - */ - public String getParameter(String name) { - - parseParameters(); - - Object value = parameters.get(name); - if (value == null) - return (null); - else if (value instanceof String[]) - return (((String[]) value)[0]); - else if (value instanceof String) - return ((String) value); - else - return (value.toString()); - - } - - - /** - * Override the getParameterMap() method of the - * wrapped request. - */ - public Map getParameterMap() { - - parseParameters(); - return (parameters); - - } - - - /** - * Override the getParameterNames() method of the - * wrapped request. - */ - public Enumeration getParameterNames() { - - parseParameters(); - return (new Enumerator(parameters.keySet())); - - } - - - /** - * Override the getParameterValues() method of the - * wrapped request. - * - * @param name Name of the requested parameter - */ - public String[] getParameterValues(String name) { - - parseParameters(); - Object value = parameters.get(name); - if (value == null) - return ((String[]) null); - else if (value instanceof String[]) - return ((String[]) value); - else if (value instanceof String) { - String values[] = new String[1]; - values[0] = (String) value; - return (values); - } else { - String values[] = new String[1]; - values[0] = value.toString(); - return (values); - } - - } - - - /** - * Override the getPathInfo() method of the wrapped request. - */ - public String getPathInfo() { - - return (this.pathInfo); - - } - - - /** - * Override the getQueryString() method of the wrapped - * request. - */ - public String getQueryString() { - - return (this.queryString); - - } - - - /** - * Override the getRequestURI() method of the wrapped - * request. - */ - public String getRequestURI() { - - return (this.requestURI); - - } - - - /** - * Override the getRequestURL() method of the wrapped - * request. - */ - public StringBuffer getRequestURL() { - - StringBuffer url = new StringBuffer(); - String scheme = getScheme(); - int port = getServerPort(); - if (port < 0) - port = 80; // Work around java.net.URL bug - - url.append(scheme); - url.append("://"); - url.append(getServerName()); - if ((scheme.equals("http") && (port != 80)) - || (scheme.equals("https") && (port != 443))) { - url.append(':'); - url.append(port); - } - url.append(getRequestURI()); - - return (url); - - } - - - /** - * Override the getServletPath() method of the wrapped - * request. - */ - public String getServletPath() { - - return (this.servletPath); - - } - - - /** - * Return the session associated with this Request, creating one - * if necessary. - */ - public HttpSession getSession() { - return (getSession(true)); - } - - - /** - * Return the session associated with this Request, creating one - * if necessary and requested. - * - * @param create Create a new session if one does not exist - */ - public HttpSession getSession(boolean create) { - - if (crossContext) { - - // There cannot be a session if no context has been assigned yet - if (context == null) - return (null); - UserSessionManager manager = context.getManager(); - // Return the current session if it exists and is valid - if (session != null && manager.isValid(session)) { - return session; - } - - HttpSession other = super.getSession(false); - if (create && (other == null)) { - // First create a session in the first context: the problem is - // that the top level request is the only one which can - // create the cookie safely - other = super.getSession(true); - } - if (other != null) { - HttpSession localSession = null; - try { - localSession = - manager.findSession(other.getId()); - } catch (IOException e) { - // Ignore - } - if (localSession == null && create) { - localSession = - context.getManager().createSession(other.getId()); - } - if (localSession != null) { - context.getManager().access(localSession); - session = localSession; - return session; - } - } - return null; - - } else { - return super.getSession(create); - } - - } - - - /** - * Returns true if the request specifies a JSESSIONID that is valid within - * the context of this ApplicationHttpRequest, false otherwise. - * - * @return true if the request specifies a JSESSIONID that is valid within - * the context of this ApplicationHttpRequest, false otherwise. - */ - public boolean isRequestedSessionIdValid() { - - if (crossContext) { - - String requestedSessionId = getRequestedSessionId(); - if (requestedSessionId == null) - return (false); - if (context == null) - return (false); - UserSessionManager manager = context.getManager(); - if (manager == null) - return (false); - HttpSession session = null; - try { - session = manager.findSession(requestedSessionId); - } catch (IOException e) { - session = null; - } - if ((session != null) && manager.isValid(session)) { - return (true); - } else { - return (false); - } - - } else { - return super.isRequestedSessionIdValid(); - } - } - - - // -------------------------------------------------------- Package Methods - - - /** - * Recycle this request - */ - public void recycle() { - if (session != null) { - context.getManager().endAccess(session); - } - } - - - /** - * Return descriptive information about this implementation. - */ - public String getInfo() { - - return (info); - - } - - - /** - * Perform a shallow copy of the specified Map, and return the result. - * - * @param orig Origin Map to be copied - */ - Map copyMap(Map orig) { - - if (orig == null) - return (new HashMap()); - HashMap dest = new HashMap(); - Iterator keys = orig.keySet().iterator(); - while (keys.hasNext()) { - String key = (String) keys.next(); - dest.put(key, orig.get(key)); - } - return (dest); - - } - - - /** - * Set the context path for this request. - * - * @param contextPath The new context path - */ - void setContextPath(String contextPath) { - - this.contextPath = contextPath; - - } - - - /** - * Set the path information for this request. - * - * @param pathInfo The new path info - */ - void setPathInfo(String pathInfo) { - - this.pathInfo = pathInfo; - - } - - - /** - * Set the query string for this request. - * - * @param queryString The new query string - */ - void setQueryString(String queryString) { - - this.queryString = queryString; - - } - - - /** - * Set the request that we are wrapping. - * - * @param request The new wrapped request - */ - void setRequest(HttpServletRequest request) { - - super.setRequest(request); - - // Initialize the attributes for this request - dispatcherType = request.getAttribute(ServletRequestImpl.DISPATCHER_TYPE_ATTR); - requestDispatcherPath = - request.getAttribute(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR); - - // Initialize the path elements for this request - contextPath = request.getContextPath(); - pathInfo = request.getPathInfo(); - queryString = request.getQueryString(); - requestURI = request.getRequestURI(); - servletPath = request.getServletPath(); - - } - - - /** - * Set the request URI for this request. - * - * @param requestURI The new request URI - */ - void setRequestURI(String requestURI) { - - this.requestURI = requestURI; - - } - - - /** - * Set the servlet path for this request. - * - * @param servletPath The new servlet path - */ - void setServletPath(String servletPath) { - - this.servletPath = servletPath; - - } - - - /** - * Parses the parameters of this request. - * - * If parameters are present in both the query string and the request - * content, they are merged. - */ - void parseParameters() { - - if (parsedParams) { - return; - } - - parameters = new HashMap(); - parameters = copyMap(getRequest().getParameterMap()); - mergeParameters(); - parsedParams = true; - } - - - /** - * Save query parameters for this request. - * - * @param queryString The query string containing parameters for this - * request - */ - void setQueryParams(String queryString) { - this.queryParamString = queryString; - } - - - // ------------------------------------------------------ Protected Methods - - - /** - * Is this attribute name one of the special ones that is added only for - * included servlets? - * - * @param name Attribute name to be tested - */ - protected boolean isSpecial(String name) { - - for (int i = 0; i < specials.length; i++) { - if (specials[i].equals(name)) - return (true); - } - return (false); - - } - - - /** - * Get a special attribute. - * - * @return the special attribute pos, or -1 if it is not a special - * attribute - */ - protected int getSpecial(String name) { - for (int i = 0; i < specials.length; i++) { - if (specials[i].equals(name)) { - return (i); - } - } - return (-1); - } - - - /** - * Set a special attribute. - * - * @return true if the attribute was a special attribute, false otherwise - */ - protected boolean setSpecial(String name, Object value) { - for (int i = 0; i < specials.length; i++) { - if (specials[i].equals(name)) { - specialAttributes[i] = value; - return (true); - } - } - return (false); - } - - - /** - * Remove a special attribute. - * - * @return true if the attribute was a special attribute, false otherwise - */ - protected boolean removeSpecial(String name) { - for (int i = 0; i < specials.length; i++) { - if (specials[i].equals(name)) { - specialAttributes[i] = null; - return (true); - } - } - return (false); - } - - - /** - * Merge the two sets of parameter values into a single String array. - * - * @param values1 First set of values - * @param values2 Second set of values - */ - protected String[] mergeValues(Object values1, Object values2) { - - ArrayList results = new ArrayList(); - - if (values1 == null) - ; - else if (values1 instanceof String) - results.add(values1); - else if (values1 instanceof String[]) { - String values[] = (String[]) values1; - for (int i = 0; i < values.length; i++) - results.add(values[i]); - } else - results.add(values1.toString()); - - if (values2 == null) - ; - else if (values2 instanceof String) - results.add(values2); - else if (values2 instanceof String[]) { - String values[] = (String[]) values2; - for (int i = 0; i < values.length; i++) - results.add(values[i]); - } else - results.add(values2.toString()); - - String values[] = new String[results.size()]; - return ((String[]) results.toArray(values)); - - } - - - // ------------------------------------------------------ Private Methods - - - /** - * Merge the parameters from the saved query parameter string (if any), and - * the parameters already present on this request (if any), such that the - * parameter values from the query string show up first if there are - * duplicate parameter names. - */ - private void mergeParameters() { - - if ((queryParamString == null) || (queryParamString.length() < 1)) - return; - - HashMap queryParameters = new HashMap(); - String encoding = getCharacterEncoding(); - if (encoding == null) - encoding = "ISO-8859-1"; - try { - RequestUtil.parseParameters - (queryParameters, queryParamString, encoding); - } catch (Exception e) { - ; - } - Iterator keys = parameters.keySet().iterator(); - while (keys.hasNext()) { - String key = (String) keys.next(); - Object value = queryParameters.get(key); - if (value == null) { - queryParameters.put(key, parameters.get(key)); - continue; - } - queryParameters.put - (key, mergeValues(value, parameters.get(key))); - } - parameters = queryParameters; - - } - - - // ----------------------------------- AttributeNamesEnumerator Inner Class - - - /** - * Utility class used to expose the special attributes as being available - * as request attributes. - */ - protected class AttributeNamesEnumerator implements Enumeration { - - protected int pos = -1; - protected int last = -1; - protected Enumeration parentEnumeration = null; - protected String next = null; - - public AttributeNamesEnumerator() { - parentEnumeration = getRequest().getAttributeNames(); - for (int i = 0; i < specialAttributes.length; i++) { - if (getAttribute(specials[i]) != null) { - last = i; - } - } - } - - public boolean hasMoreElements() { - return ((pos != last) || (next != null) - || ((next = findNext()) != null)); - } - - public Object nextElement() { - if (pos != last) { - for (int i = pos + 1; i <= last; i++) { - if (getAttribute(specials[i]) != null) { - pos = i; - return (specials[i]); - } - } - } - String result = next; - if (next != null) { - next = findNext(); - } else { - throw new NoSuchElementException(); - } - return result; - } - - protected String findNext() { - String result = null; - while ((result == null) && (parentEnumeration.hasMoreElements())) { - String current = (String) parentEnumeration.nextElement(); - if (!isSpecial(current)) { - result = current; - } - } - return result; - } - - } - - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletResponseImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletResponseImpl.java deleted file mode 100644 index d876697ec..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletResponseImpl.java +++ /dev/null @@ -1,1463 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - - -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.net.MalformedURLException; -import java.net.URL; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Locale; -import java.util.TimeZone; -import java.util.Vector; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.apache.tomcat.util.buf.CharChunk; -import org.apache.tomcat.util.buf.MessageBytes; -import org.apache.tomcat.util.buf.UEncoder; -import org.apache.tomcat.util.http.FastHttpDateFormat; -import org.apache.tomcat.util.http.HttpResponse; -import org.apache.tomcat.util.http.MimeHeaders; -import org.apache.tomcat.util.http.ServerCookie; - -/** - * Wrapper object for the Coyote response. - * - * @author Remy Maucherat - * @author Craig R. McClanahan - * @version $Revision$ $Date$ - */ - -public class ServletResponseImpl - implements HttpServletResponse { - - /** - * Format for http response header date field - * From DateTool - */ - public static final String HTTP_RESPONSE_DATE_HEADER = - "EEE, dd MMM yyyy HH:mm:ss zzz"; - - // ----------------------------------------------------------- Constructors - - - ServletResponseImpl() { - urlEncoder.addSafeCharacter('/'); - } - - - /** - * The date format we will use for creating date headers. - */ - protected SimpleDateFormat format = null; - - - /** - * The associated output buffer. - */ - protected BodyWriter outputBuffer; - - - /** - * The associated output stream. - */ - protected ServletOutputStreamImpl outputStream; - - - /** - * The associated writer. - */ - protected PrintWriter writer; - - - /** - * The application commit flag. - */ - protected boolean appCommitted = false; - - - /** - * The included flag. - */ - protected boolean included = false; - - - /** - * The characterEncoding flag - */ - private boolean isCharacterEncodingSet = false; - - /** - * The error flag. - */ - protected boolean error = false; - - - /** - * The set of Cookies associated with this Response. - */ - protected ArrayList cookies = new ArrayList(); - - - /** - * Using output stream flag. - */ - protected boolean usingOutputStream = false; - - - /** - * Using writer flag. - */ - protected boolean usingWriter = false; - - - /** - * The request with which this response is associated. - */ - protected ServletRequestImpl req = null; - - /** - * URL encoder. - */ - protected UEncoder urlEncoder = new UEncoder(); - - - /** - * Recyclable buffer to hold the redirect URL. - */ - protected CharChunk redirectURLCC = new CharChunk(1024); - - - private HttpResponse resB; - - - // Cached/derived information - reflected in headers - protected static Locale DEFAULT_LOCALE = Locale.getDefault(); - - public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1"; - - protected Locale locale = DEFAULT_LOCALE; - - // XXX - protected boolean commited = false; - protected String contentType = null; - - /** - * Has the charset been explicitly set. - */ - protected boolean charsetSet = false; - protected String characterEncoding = DEFAULT_CHARACTER_ENCODING; - - - // --------------------------------------------------------- Public Methods - - - /** - * Release all object references, and initialize instance variables, in - * preparation for reuse of this object. - */ - public void recycle() { - - usingOutputStream = false; - usingWriter = false; - appCommitted = false; - commited = false; - included = false; - error = false; - isCharacterEncodingSet = false; - - cookies.clear(); - - outputBuffer.recycle(); - - resB.recycle(); - } - - - // ------------------------------------------------------- Response Methods - - - /** - * Return the number of bytes actually written to the output stream. - */ - public int getContentCount() { - return outputBuffer.getBytesWritten() + outputBuffer.getCharsWritten(); - } - - - /** - * Set the application commit flag. - * - * @param appCommitted The new application committed flag value - */ - public void setAppCommitted(boolean appCommitted) { - this.appCommitted = appCommitted; - } - - - /** - * Application commit flag accessor. - */ - public boolean isAppCommitted() { - return (this.appCommitted || isCommitted() || isSuspended() - || ((getHttpResponse().getContentLength() > 0) - && (getContentCount() >= getHttpResponse().getContentLength()))); - } - - - /** - * Return the "processing inside an include" flag. - */ - public boolean getIncluded() { - return included; - } - - - /** - * Set the "processing inside an include" flag. - * - * @param included true if we are currently inside a - * RequestDispatcher.include(), else false - */ - public void setIncluded(boolean included) { - this.included = included; - } - - - /** - * Return the Request with which this Response is associated. - */ - public ServletRequestImpl getRequest() { - return (this.req); - } - - /** - * Set the Request with which this Response is associated. - * - * @param request The new associated request - */ - public void setRequest(ServletRequestImpl request) { - this.req = (ServletRequestImpl) request; - } - - - /** - * Return the output stream associated with this Response. - */ - public OutputStream getStream() { - return outputStream; - } - - - /** - * Set the output stream associated with this Response. - * - * @param stream The new output stream - */ - public void setStream(OutputStream stream) { - // This method is evil - } - - - /** - * Set the suspended flag. - * - * @param suspended The new suspended flag value - */ - public void setSuspended(boolean suspended) throws IOException { - //coyoteResponse.setCommitted(true); - flushBuffer(); - outputBuffer.setSuspended(suspended); - } - - - /** - * Suspended flag accessor. - */ - public boolean isSuspended() { - return outputBuffer.isSuspended(); - } - - - /** - * Set the error flag. - */ - public void setError() { - error = true; - } - - - /** - * Error flag accessor. - */ - public boolean isError() { - return error; - } - - - /** - * Create and return a ServletOutputStream to write the content - * associated with this Response. - * - * @exception IOException if an input/output error occurs - */ - public ServletOutputStream createOutputStream() - throws IOException { - // Probably useless - return outputStream; - } - - /** - * Return the content type that was set or calculated for this response, - * or null if no content type was set. - */ - public String getContentType() { - String ret = contentType; - - if (ret != null - && characterEncoding != null - && charsetSet) { - ret = ret + ";charset=" + characterEncoding; - } - - return ret; - } - - - /** - * Return a PrintWriter that can be used to render error messages, - * regardless of whether a stream or writer has already been acquired. - * - * @return Writer which can be used for error reports. If the response is - * not an error report returned using sendError or triggered by an - * unexpected exception thrown during the servlet processing - * (and only in that case), null will be returned if the response stream - * has already been used. - * - * @exception IOException if an input/output error occurs - */ - public PrintWriter getReporter() throws IOException { - if (outputBuffer.isNew()) { - outputBuffer.checkConverter(); - if (writer == null) { - writer = new ServletWriterImpl(outputBuffer); - } - return writer; - } else { - return null; - } - } - - - // ------------------------------------------------ ServletResponse Methods - - - /** - * Flush the buffer and commit this response. - * - * @exception IOException if an input/output error occurs - */ - public void flushBuffer() - throws IOException { - outputBuffer.flush(); - } - - - /** - * Return the actual buffer size used for this Response. - */ - public int getBufferSize() { - return outputBuffer.getBufferSize(); - } - - - /** - * Return the character encoding used for this Response. - */ - public String getCharacterEncoding() { - return characterEncoding; - } - - - /** - * Return the servlet output stream associated with this Response. - * - * @exception IllegalStateException if getWriter has - * already been called for this response - * @exception IOException if an input/output error occurs - */ - public ServletOutputStream getOutputStream() - throws IOException { - - if (usingWriter) - throw new IllegalStateException - ("usingWriter"); - - usingOutputStream = true; - return outputStream; - - } - - public BodyWriter getBodyWriter() { - return outputBuffer; - } - - /** - * Return the Locale assigned to this response. - */ - public Locale getLocale() { - return locale; - } - - - /** - * Return the writer associated with this Response. - * - * @exception IllegalStateException if getOutputStream has - * already been called for this response - * @exception IOException if an input/output error occurs - */ - public PrintWriter getWriter() - throws IOException { - - if (usingOutputStream) - throw new IllegalStateException - ("usingOutputStream"); - - /* - * If the response's character encoding has not been specified as - * described in getCharacterEncoding (i.e., the method - * just returns the default value ISO-8859-1), - * getWriter updates it to ISO-8859-1 - * (with the effect that a subsequent call to getContentType() will - * include a charset=ISO-8859-1 component which will also be - * reflected in the Content-Type response header, thereby satisfying - * the Servlet spec requirement that containers must communicate the - * character encoding used for the servlet response's writer to the - * client). - */ - setCharacterEncoding(getCharacterEncoding()); - - usingWriter = true; - outputBuffer.checkConverter(); - if (writer == null) { - writer = new ServletWriterImpl(outputBuffer); - } - return writer; - - } - - - /** - * Has the output of this response already been committed? - */ - public boolean isCommitted() { - return getHttpResponse().isCommitted(); - } - - /** - * Clear any content written to the buffer. - * - * @exception IllegalStateException if this response has already - * been committed - */ - public void reset() { - - if (included) - return; // Ignore any call from an included servlet - - if (isCommitted()) - throw new IllegalStateException("isCommitted"); - - resB.recycle(); // reset headers, status code, message - req.getConnector().reset(this); - contentType = null; - locale = DEFAULT_LOCALE; - characterEncoding = DEFAULT_CHARACTER_ENCODING; - charsetSet = false; - - outputBuffer.reset(); - } - - - /** - * Reset the data buffer but not any status or header information. - * - * @exception IllegalStateException if the response has already - * been committed - */ - public void resetBuffer() { - - if (isCommitted()) - throw new IllegalStateException("isCommitted"); - - outputBuffer.reset(); - - } - - - /** - * Set the buffer size to be used for this Response. - * - * @param size The new buffer size - * - * @exception IllegalStateException if this method is called after - * output has been committed for this response - */ - public void setBufferSize(int size) { - - if (isCommitted() || !outputBuffer.isNew()) - throw new IllegalStateException - ("isCommitted || !isNew"); - - outputBuffer.setBufferSize(size); - - } - - - /** - * Set the content length (in bytes) for this Response. - * Ignored for writers if non-ISO-8859-1 encoding ( we could add more - * encodings that are constant. - */ - public void setContentLength(int length) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - // writers can use variable-length encoding. - if (usingWriter && !"ISO-8859-1".equals(getCharacterEncoding())) { - return; - } - getHttpResponse().setContentLength(length); - - } - - - /** - * Set the content type for this Response. - * - * @param type The new content type - */ - public void setContentType(String type) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - // Ignore charset if getWriter() has already been called - if (usingWriter) { - if (type != null) { - int index = type.indexOf(";"); - if (index != -1) { - type = type.substring(0, index); - } - } - } - - getHttpResponse().setContentType(type); - - // Check to see if content type contains charset - if (type != null) { - int index = type.indexOf(";"); - if (index != -1) { - int len = type.length(); - index++; - while (index < len && Character.isSpace(type.charAt(index))) { - index++; - } - if (index+7 < len - && type.charAt(index) == 'c' - && type.charAt(index+1) == 'h' - && type.charAt(index+2) == 'a' - && type.charAt(index+3) == 'r' - && type.charAt(index+4) == 's' - && type.charAt(index+5) == 'e' - && type.charAt(index+6) == 't' - && type.charAt(index+7) == '=') { - isCharacterEncodingSet = true; - } - } - } - } - - - /* - * Overrides the name of the character encoding used in the body - * of the request. This method must be called prior to reading - * request parameters or reading input using getReader(). - * - * @param charset String containing the name of the chararacter encoding. - */ - public void setCharacterEncoding(String charset) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - // Ignore any call made after the getWriter has been invoked - // The default should be used - if (usingWriter) - return; - - if (isCommitted()) - return; - if (charset == null) - return; - - characterEncoding = charset; - charsetSet=true; - isCharacterEncodingSet = true; - } - - - - /** - * Set the Locale that is appropriate for this response, including - * setting the appropriate character encoding. - * - * @param locale The new locale - */ - public void setLocale(Locale locale) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - if (locale == null) { - return; // throw an exception? - } - - // Save the locale for use by getLocale() - this.locale = locale; - - // Set the contentLanguage for header output - String contentLanguage = locale.getLanguage(); - if ((contentLanguage != null) && (contentLanguage.length() > 0)) { - String country = locale.getCountry(); - StringBuffer value = new StringBuffer(contentLanguage); - if ((country != null) && (country.length() > 0)) { - value.append('-'); - value.append(country); - } - contentLanguage = value.toString(); - } - resB.setHeader("Content-Language", contentLanguage); - - // Ignore any call made after the getWriter has been invoked. - // The default should be used - if (usingWriter) - return; - - if (isCharacterEncodingSet) { - return; - } - - Locale2Charset cm = req.getContext().getCharsetMapper(); - String charset = cm.getCharset( locale ); - if ( charset != null ){ - setCharacterEncoding(charset); - } - - } - - - // --------------------------------------------------- HttpResponse Methods - - - /** - * Return an array of all cookies set for this response, or - * a zero-length array if no cookies have been set. - */ - public Cookie[] getCookies() { - return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()])); - } - - - /** - * Return the value for the specified header, or null if this - * header has not been set. If more than one value was added for this - * name, only the first is returned; use getHeaderValues() to retrieve all - * of them. - * - * @param name Header name to look up - */ - public String getHeader(String name) { - return getHttpResponse().getMimeHeaders().getHeader(name); - } - - - /** - * Return an array of all the header names set for this response, or - * a zero-length array if no headers have been set. - */ - public Collection getHeaderNames() { - - MimeHeaders headers = getHttpResponse().getMimeHeaders(); - int n = headers.size(); - ArrayList result = new ArrayList(); - for (int i = 0; i < n; i++) { - result.add(headers.getName(i).toString()); - } - return result; - } - - - /** - * Return an array of all the header values associated with the - * specified header name, or an zero-length array if there are no such - * header values. - * - * @param name Header name to look up - */ - public String[] getHeaderValues(String name) { - - Enumeration enumeration = getHttpResponse().getMimeHeaders().values(name); - Vector result = new Vector(); - while (enumeration.hasMoreElements()) { - result.addElement(enumeration.nextElement()); - } - String[] resultArray = new String[result.size()]; - result.copyInto(resultArray); - return resultArray; - - } - - - /** - * Return the error message that was set with sendError() - * for this Response. - */ - public String getMessage() { - return getHttpResponse().getMessage(); - } - - - /** - * Return the HTTP status code associated with this Response. - */ - public int getStatus() { - return getHttpResponse().getStatus(); - } - - - /** - * Reset this response, and specify the values for the HTTP status code - * and corresponding message. - * - * @exception IllegalStateException if this response has already been - * committed - */ - public void reset(int status, String message) { - reset(); - setStatus(status, message); - } - - - // -------------------------------------------- HttpServletResponse Methods - - - /** - * Add the specified Cookie to those that will be included with - * this Response. - * - * @param cookie Cookie to be added - */ - public void addCookie(final Cookie cookie) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - cookies.add(cookie); - - final StringBuffer sb = new StringBuffer(); - ServerCookie.appendCookieValue - (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(), - cookie.getPath(), cookie.getDomain(), cookie.getComment(), - cookie.getMaxAge(), cookie.getSecure(), false); - - // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 ) - // RFC2965 is not supported by browsers and the Servlet spec - // asks for 2109. - addHeader("Set-Cookie", sb.toString()); - - } - - - /** - * Add the specified date header to the specified value. - * - * @param name Name of the header to set - * @param value Date value to be set - */ - public void addDateHeader(String name, long value) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) { - return; - } - - if (format == null) { - format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER, - Locale.US); - format.setTimeZone(TimeZone.getTimeZone("GMT")); - } - - addHeader(name, FastHttpDateFormat.formatDate(value, format)); - - } - - - /** - * Add the specified header to the specified value. - * - * @param name Name of the header to set - * @param value Value to be set - */ - public void addHeader(String name, String value) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - getHttpResponse().addHeader(name, value); - - } - - - /** - * Add the specified integer header to the specified value. - * - * @param name Name of the header to set - * @param value Integer value to be set - */ - public void addIntHeader(String name, int value) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - addHeader(name, "" + value); - - } - - - /** - * Has the specified header been set already in this response? - * - * @param name Name of the header to check - */ - public boolean containsHeader(String name) { - // Need special handling for Content-Type and Content-Length due to - // special handling of these in coyoteResponse - char cc=name.charAt(0); - if(cc=='C' || cc=='c') { - if(name.equalsIgnoreCase("Content-Type")) { - // Will return null if this has not been set - return getContentType() != null; - } - if(name.equalsIgnoreCase("Content-Length")) { - // -1 means not known and is not sent to client - return (getHttpResponse().getContentLength() != -1); - } - } - - return getHttpResponse().containsHeader(name); - } - - - /** - * Encode the session identifier associated with this response - * into the specified redirect URL, if necessary. - * - * @param url URL to be encoded - */ - public String encodeRedirectURL(String url) { - - if (isEncodeable(toAbsolute(url))) { - return (toEncoded(url, req.getSession().getId())); - } else { - return (url); - } - - } - - - /** - * Encode the session identifier associated with this response - * into the specified redirect URL, if necessary. - * - * @param url URL to be encoded - * - * @deprecated As of Version 2.1 of the Java Servlet API, use - * encodeRedirectURL() instead. - */ - public String encodeRedirectUrl(String url) { - return (encodeRedirectURL(url)); - } - - - /** - * Encode the session identifier associated with this response - * into the specified URL, if necessary. - * - * @param url URL to be encoded - */ - public String encodeURL(String url) { - - String absolute = toAbsolute(url); - if (isEncodeable(absolute)) { - // W3c spec clearly said - if (url.equalsIgnoreCase("")){ - url = absolute; - } - return (toEncoded(url, req.getSession().getId())); - } else { - return (url); - } - - } - - - /** - * Encode the session identifier associated with this response - * into the specified URL, if necessary. - * - * @param url URL to be encoded - * - * @deprecated As of Version 2.1 of the Java Servlet API, use - * encodeURL() instead. - */ - public String encodeUrl(String url) { - return (encodeURL(url)); - } - - - /** - * Send an acknowledgment of a request. - * - * @exception IOException if an input/output error occurs - */ - public void sendAcknowledgement() - throws IOException { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - req.getConnector().acknowledge(this); - } - - - /** - * Send an error response with the specified status and a - * default message. - * - * @param status HTTP status code to send - * - * @exception IllegalStateException if this response has - * already been committed - * @exception IOException if an input/output error occurs - */ - public void sendError(int status) - throws IOException { - sendError(status, null); - } - - - /** - * Send an error response with the specified status and message. - * - * @param status HTTP status code to send - * @param message Corresponding message to send - * - * @exception IllegalStateException if this response has - * already been committed - * @exception IOException if an input/output error occurs - */ - public void sendError(int status, String message) - throws IOException { - - if (isCommitted()) - throw new IllegalStateException - ("isCommitted"); - - // Ignore any call from an included servlet - if (included) - return; - - setError(); - - getHttpResponse().setStatus(status); - getHttpResponse().setMessage(message); - - // Clear any data content that has been buffered - resetBuffer(); - - // Cause the response to be finished (from the application perspective) - String statusPage = req.getContext().findStatusPage(status); - - if (statusPage != null) { - req.getContext().handleStatusPage(req, this, status, statusPage); - } else { - // Send a default message body. - // TODO: maybe other mechanism to customize default. - defaultStatusPage(status, message); - } - setSuspended(true); - } - - /** - * Default handler for status code != 200 - */ - void defaultStatusPage(int status, String message) - throws IOException { - setContentType("text/html"); - if (status > 400 && status < 600) { - if (getOutputBuffer().getBytesWritten() == 0) { - getOutputBuffer().write("

Status: " + - status + "

Message: " + message + - "

"); - getOutputBuffer().flush(); - } - } - } - - - - /** - * Send a temporary redirect to the specified redirect location URL. - * - * @param location Location URL to redirect to - * - * @exception IllegalStateException if this response has - * already been committed - * @exception IOException if an input/output error occurs - */ - public void sendRedirect(String location) - throws IOException { - - if (isCommitted()) - throw new IllegalStateException - ("isCommitted"); - - // Ignore any call from an included servlet - if (included) - return; - - // Clear any data content that has been buffered - resetBuffer(); - - // Generate a temporary redirect to the specified location - try { - String absolute = toAbsolute(location); - setStatus(SC_FOUND); - setHeader("Location", absolute); - } catch (IllegalArgumentException e) { - setStatus(SC_NOT_FOUND); - } - - // Cause the response to be finished (from the application perspective) - setSuspended(true); - - } - - - /** - * Set the specified date header to the specified value. - * - * @param name Name of the header to set - * @param value Date value to be set - */ - public void setDateHeader(String name, long value) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) { - return; - } - - if (format == null) { - format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER, - Locale.US); - format.setTimeZone(TimeZone.getTimeZone("GMT")); - } - - setHeader(name, FastHttpDateFormat.formatDate(value, format)); - - } - - - /** - * Set the specified header to the specified value. - * - * @param name Name of the header to set - * @param value Value to be set - */ - public void setHeader(String name, String value) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - getHttpResponse().setHeader(name, value); - - } - - - /** - * Set the specified integer header to the specified value. - * - * @param name Name of the header to set - * @param value Integer value to be set - */ - public void setIntHeader(String name, int value) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - setHeader(name, "" + value); - - } - - - /** - * Set the HTTP status to be returned with this response. - * - * @param status The new HTTP status - */ - public void setStatus(int status) { - setStatus(status, null); - } - - - /** - * Set the HTTP status and message to be returned with this response. - * - * @param status The new HTTP status - * @param message The associated text message - * - * @deprecated As of Version 2.1 of the Java Servlet API, this method - * has been deprecated due to the ambiguous meaning of the message - * parameter. - */ - public void setStatus(int status, String message) { - - if (isCommitted()) - return; - - // Ignore any call from an included servlet - if (included) - return; - - getHttpResponse().setStatus(status); - getHttpResponse().setMessage(message); - - } - - - // ------------------------------------------------------ Protected Methods - - - /** - * Return true if the specified URL should be encoded with - * a session identifier. This will be true if all of the following - * conditions are met: - *
    - *
  • The request we are responding to asked for a valid session - *
  • The requested session ID was not received via a cookie - *
  • The specified URL points back to somewhere within the web - * application that is responding to this request - *
- * - * @param location Absolute URL to be validated - */ - protected boolean isEncodeable(final String location) { - - if (location == null) - return (false); - - // Is this an intra-document reference? - if (location.startsWith("#")) - return (false); - - // Are we in a valid session that is not using cookies? - final ServletRequestImpl hreq = req; - final HttpSession session = hreq.getSession(false); - if (session == null) - return (false); - if (hreq.isRequestedSessionIdFromCookie()) - return (false); - - // Is this a valid absolute URL? - URL url = null; - try { - url = new URL(location); - } catch (MalformedURLException e) { - return (false); - } - - // Does this URL match down to (and including) the context path? - if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol())) - return (false); - if (!hreq.getServerName().equalsIgnoreCase(url.getHost())) - return (false); - int serverPort = hreq.getServerPort(); - if (serverPort == -1) { - if ("https".equals(hreq.getScheme())) - serverPort = 443; - else - serverPort = 80; - } - int urlPort = url.getPort(); - if (urlPort == -1) { - if ("https".equals(url.getProtocol())) - urlPort = 443; - else - urlPort = 80; - } - if (serverPort != urlPort) - return (false); - - String contextPath = req.getContext().getContextPath(); - if (contextPath != null) { - String file = url.getFile(); - if ((file == null) || !file.startsWith(contextPath)) - return (false); - if( file.indexOf(";jsessionid=" + session.getId()) >= 0 ) - return (false); - } - - // This URL belongs to our web application, so it is encodeable - return (true); - - } - - - /** - * Convert (if necessary) and return the absolute URL that represents the - * resource referenced by this possibly relative URL. If this URL is - * already absolute, return it unchanged. - * - * @param location URL to be (possibly) converted and then returned - * - * @exception IllegalArgumentException if a MalformedURLException is - * thrown when converting the relative URL to an absolute one - */ - private String toAbsolute(String location) { - - if (location == null) - return (location); - - boolean leadingSlash = location.startsWith("/"); - - if (leadingSlash || !hasScheme(location)) { - - redirectURLCC.recycle(); - - String scheme = req.getScheme(); - String name = req.getServerName(); - int port = req.getServerPort(); - - try { - redirectURLCC.append(scheme, 0, scheme.length()); - redirectURLCC.append("://", 0, 3); - redirectURLCC.append(name, 0, name.length()); - if ((scheme.equals("http") && port != 80) - || (scheme.equals("https") && port != 443)) { - redirectURLCC.append(':'); - String portS = port + ""; - redirectURLCC.append(portS, 0, portS.length()); - } - if (!leadingSlash) { - String relativePath = req.getDecodedRequestURI(); - int pos = relativePath.lastIndexOf('/'); - relativePath = relativePath.substring(0, pos); - - String encodedURI = null; - encodedURI = urlEncoder.encodeURL(relativePath); - redirectURLCC.append(encodedURI, 0, encodedURI.length()); - redirectURLCC.append('/'); - } - redirectURLCC.append(location, 0, location.length()); - } catch (IOException e) { - IllegalArgumentException iae = - new IllegalArgumentException(location); - iae.initCause(e); - throw iae; - } - - return redirectURLCC.toString(); - - } else { - - return (location); - - } - - } - - - /** - * Determine if a URI string has a scheme component. - */ - private boolean hasScheme(String uri) { - int len = uri.length(); - for(int i=0; i < len ; i++) { - char c = uri.charAt(i); - if(c == ':') { - return i > 0; - } else if(!isSchemeChar(c)) { - return false; - } - } - return false; - } - - /** - * Determine if the character is allowed in the scheme of a URI. - * See RFC 2396, Section 3.1 - */ - private static boolean isSchemeChar(char c) { - return Character.isLetterOrDigit(c) || - c == '+' || c == '-' || c == '.'; - } - - - /** - * Return the specified URL with the specified session identifier - * suitably encoded. - * - * @param url URL to be encoded with the session id - * @param sessionId Session id to be included in the encoded URL - */ - protected String toEncoded(String url, String sessionId) { - - if ((url == null) || (sessionId == null)) - return (url); - - String path = url; - String query = ""; - String anchor = ""; - int question = url.indexOf('?'); - if (question >= 0) { - path = url.substring(0, question); - query = url.substring(question); - } - int pound = path.indexOf('#'); - if (pound >= 0) { - anchor = path.substring(pound); - path = path.substring(0, pound); - } - StringBuffer sb = new StringBuffer(path); - if( sb.length() > 0 ) { // jsessionid can't be first. - sb.append(";jsessionid="); - sb.append(sessionId); - } - sb.append(anchor); - sb.append(query); - return (sb.toString()); - - } - - - public int getBytesWritten() { - return outputBuffer.getBytesWritten(); - } - - public BodyWriter getOutputBuffer() { - return outputBuffer; - } - - public void setWriter(BodyWriter ob) { - outputBuffer = ob; - outputStream = new ServletOutputStreamImpl(outputBuffer); - } - - public CharSequence getResponseHeader(String name) { - MessageBytes v = getHttpResponse().getMimeHeaders().getValue(name); - return (v == null) ? null : v.toString(); - } - - - public HttpResponse getHttpResponse() { - return resB; - } - - - public void setHttpResponse(HttpResponse resB, BodyWriter ob) { - this.resB = resB; - setWriter(ob); - } - - - - @Override - public Collection getHeaders(String name) { - return null; - } - - -} - diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletResponseIncludeWrapper.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletResponseIncludeWrapper.java deleted file mode 100644 index 6f8b26077..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletResponseIncludeWrapper.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.tomcat.lite; - - -import java.io.IOException; -import java.util.Locale; - -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; - - -/** - * Wrapper around the response object received as parameter to - * RequestDispatcher.include(). - * - * @author Costin Manolache - */ -public class ServletResponseIncludeWrapper extends HttpServletResponseWrapper { - public ServletResponseIncludeWrapper(ServletResponse current) { - super((HttpServletResponse) current); - } - - // Not overriden: - /* - public boolean containsHeader(String name) - public String encodeRedirectUrl(String url) - public String encodeRedirectURL(String url) - public String encodeUrl(String url) - public String encodeURL(String url) - public void flushBuffer() throws IOException - public int getBufferSize() - public String getCharacterEncoding() - public String getContentType() - public Locale getLocale() - public ServletOutputStream getOutputStream() throws IOException - public ServletResponse getResponse() - public PrintWriter getWriter() throws IOException - public boolean isCommitted() - public void resetBuffer() - public void setCharacterEncoding(String charset) - public void setResponse(ServletResponse response) - */ - - public void reset() { - if (getResponse().isCommitted()) - getResponse().reset(); - else - throw new IllegalStateException(); - } - - public void setContentLength(int len) { - } - - public void setContentType(String type) { - } - - public void setLocale(Locale loc) { - } - - public void setBufferSize(int size) { - } - - public void addCookie(Cookie cookie) { - } - - public void addDateHeader(String name, long value) { - } - - public void addHeader(String name, String value) { - } - - public void addIntHeader(String name, int value) { - } - - public void sendError(int sc) throws IOException { - } - - public void sendError(int sc, String msg) throws IOException { - } - - public void sendRedirect(String location) throws IOException { - } - - public void setDateHeader(String name, long value) { - } - - public void setHeader(String name, String value) { - } - - public void setIntHeader(String name, int value) { - } - - public void setStatus(int sc) { - } - - public void setStatus(int sc, String msg) { - } -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletWriterImpl.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletWriterImpl.java deleted file mode 100644 index a27d69301..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/ServletWriterImpl.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * 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.tomcat.lite; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; - -/** - * Coyote implementation of the servlet writer. - * - * @author Remy Maucherat - */ -public class ServletWriterImpl - extends PrintWriter { - - - // -------------------------------------------------------------- Constants - - - private static final char[] LINE_SEP = { '\r', '\n' }; - - - // ----------------------------------------------------- Instance Variables - - - protected Writer ob; - protected boolean error = false; - - - // ----------------------------------------------------------- Constructors - - - public ServletWriterImpl(Writer ob) { - super(ob); - this.ob = ob; - } - - - // --------------------------------------------------------- Public Methods - - - /** - * Prevent cloning the facade. - */ - protected Object clone() - throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } - - - // -------------------------------------------------------- Package Methods - - - /** - * Clear facade. - */ - void clear() { - ob = null; - } - - - /** - * Recycle. - */ - void recycle() { - error = false; - } - - - // --------------------------------------------------------- Writer Methods - - - public void flush() { - - if (error) - return; - - try { - ob.flush(); - } catch (IOException e) { - error = true; - } - - } - - - public void close() { - - // We don't close the PrintWriter - super() is not called, - // so the stream can be reused. We close ob. - try { - ob.close(); - } catch (IOException ex ) { - ; - } - error = false; - - } - - - public boolean checkError() { - flush(); - return error; - } - - - public void write(int c) { - - if (error) - return; - - try { - ob.write(c); - } catch (IOException e) { - error = true; - } - - } - - - public void write(char buf[], int off, int len) { - - if (error) - return; - - try { - ob.write(buf, off, len); - } catch (IOException e) { - error = true; - } - - } - - - public void write(char buf[]) { - write(buf, 0, buf.length); - } - - - public void write(String s, int off, int len) { - - if (error) - return; - - try { - ob.write(s, off, len); - } catch (IOException e) { - error = true; - } - - } - - - public void write(String s) { - write(s, 0, s.length()); - } - - - // ---------------------------------------------------- PrintWriter Methods - - - public void print(boolean b) { - if (b) { - write("true"); - } else { - write("false"); - } - } - - - public void print(char c) { - write(c); - } - - - public void print(int i) { - write(String.valueOf(i)); - } - - - public void print(long l) { - write(String.valueOf(l)); - } - - - public void print(float f) { - write(String.valueOf(f)); - } - - - public void print(double d) { - write(String.valueOf(d)); - } - - - public void print(char s[]) { - write(s); - } - - - public void print(String s) { - if (s == null) { - s = "null"; - } - write(s); - } - - - public void print(Object obj) { - write(String.valueOf(obj)); - } - - - public void println() { - write(LINE_SEP); - } - - - public void println(boolean b) { - print(b); - println(); - } - - - public void println(char c) { - print(c); - println(); - } - - - public void println(int i) { - print(i); - println(); - } - - - public void println(long l) { - print(l); - println(); - } - - - public void println(float f) { - print(f); - println(); - } - - - public void println(double d) { - print(d); - println(); - } - - - public void println(char c[]) { - print(c); - println(); - } - - - public void println(String s) { - print(s); - println(); - } - - - public void println(Object o) { - print(o); - println(); - } - - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/TomcatLite.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/TomcatLite.java deleted file mode 100644 index b01220543..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/TomcatLite.java +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import javax.servlet.Filter; -import javax.servlet.Servlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.UnavailableException; -import javax.servlet.http.HttpServletResponse; - -import org.apache.tomcat.integration.ObjectManager; -import org.apache.tomcat.integration.simple.SimpleObjectManager; -import org.apache.tomcat.util.buf.MessageBytes; -import org.apache.tomcat.util.buf.UriNormalizer; -import org.apache.tomcat.util.http.mapper.MappingData; - -/** - * Simpler, lower footprint serlvet engine. - * - * Uses ObjectManager to integate with an embedding app - * - the object names it uses: - * - * Internal objects created by Tomcat and registered for management - * and injection: - * - Servlet:CONTEXT_PATH:SERVLETNAME - a ServletWrapper - * - ServletContext:CONTEXT_PATH - * - ProtocolHandler:ep-PORT - coyote ProtocolHandler - * - CoyoteServer:CoyoteServer-PORT - * - CoyoteAdapter:PATH - for the coyote used Adapter - * - TomcatLite - this object. - * - Connector - the connector object - * - * Plugins to be constructed by framework ( defaults set in initDefaults ): - * - UserSessionManager - * - UserTemplateClassMapper - * - ContextPreinitListener - * - Connector - * - WebappServletMapper - * - WebappFilterMapper - * - default-servlet - * - jspwildcard-servlet - * - Foo-servlet - servlet named Foo - * - * - * @author Costin Manolache - */ -public class TomcatLite implements Runnable { - - private String serverDirName; - private File workDir; - - // all contexts - hostMapper knows about hostnames and how they are mapped. - // this shouldn't be needed if we want to delegate ctx management - private ArrayList contexts = new ArrayList(); - - URLClassLoader contextParentLoader; - - // Discovered or default Host/Context mapper - Filter hostMapper; - - // Servlets to preload in each context, configurable from CLI or API - Map preloadServlets = new HashMap(); - Map preloadMappings = new HashMap(); - - Map ctxDefaultInitParam = new HashMap(); - - Connector connector; - - ObjectManager om; - - static String SERVLETS_PACKAGE = "org.apache.tomcat.servlets"; - - - protected boolean daemon = false; - - public TomcatLite() { - } - - public TomcatLite(ObjectManager om) { - this.setObjectManager(om); - } - - // --------------- start/stop --------------- - - public static ObjectManager defaultObjectManager() { - SimpleObjectManager cfg = new SimpleObjectManager(); - cfg.loadResource("org/apache/tomcat/lite/config.properties"); - return cfg; - } - /** - * Return the object manager associated with this tomcat. - * If none set, create a minimal one with the default - * values. - */ - public ObjectManager getObjectManager() { - if (om == null) { - om = defaultObjectManager(); - } - return om; - } - - public void setObjectManager(ObjectManager om) { - this.om = om; - } - - public List/**/ getWebapps() { - return contexts; - } - - public URLClassLoader getContextParentLoader() { - if (contextParentLoader == null) { - - ClassLoader parent = this.getClass().getClassLoader(); - contextParentLoader = new URLClassLoader(new URL[] {}, - parent); - - /*if (engineRepo == null) { - engineRepo = new Repository(); - engineRepo.setParentClassLoader(parent); - } - - contextParentLoader = - engineRepo.getClassLoader(); - */ - } - return contextParentLoader; - } - - public void start() throws IOException { - long t0 = System.currentTimeMillis(); - - // start all contexts - // init all contexts - Iterator i1 = contexts.iterator(); - while (i1.hasNext()) { - ServletContextImpl ctx = (ServletContextImpl) i1.next(); - try { - ctx.start(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - long t1 = System.currentTimeMillis(); - System.err.println("Engine.start() " + (t1-t0)); - } - - - /** - * Add a context - used for IntrospectionUtils. - * - * ContextPath:ContextBaseDir - */ - public void setContext(String c) throws ServletException { - String[] pathDir = c.split(":", 2); - addServletContext("", pathDir[1], pathDir[0]); - } - - public void setServletContexts(List c) throws ServletException { - for (ServletContext ctx: c) { - addServletContext((ServletContextImpl) ctx); - } - } - - public void setPreload(String servletNameClass) { - String[] nv = servletNameClass.split(":"); - preloadServlets.put(nv[0], nv[1]); - } - - public void addPreload(String servletName, String servletClassName) { - preloadServlets.put(servletName, servletClassName); - } - - public void setDefaultInitParam(String nameValue) { - String[] nv = nameValue.split(":"); - ctxDefaultInitParam.put(nv[0], nv[1]); - } - - public void addDefaultInitParam(String name, String value) { - ctxDefaultInitParam.put(name, value); - } - - public void setPreloadMappings(String servletPath) { - String[] nv = servletPath.split(":"); - preloadMappings.put(nv[0], nv[1]); - } - - public void addPreloadMapping(String servletName, String path) { - preloadMappings.put(servletName, path); - } - - public void stop() { - Iterator i1 = contexts.iterator(); - while (i1.hasNext()) { - ServletContextImpl ctx = (ServletContextImpl) i1.next(); - try { - ctx.destroy(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - try { - stopConnector(); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - // -------------- Context add/remove -------------- - - public static String[] DEFAULT_WELCOME = { "index.html" }; - - public void addServletContext(ServletContextImpl ctx) throws ServletException { - ctx.setTomcat(this); - if (hostMapper == null) { - hostMapper = new WebappContextMapper(); - } - - ((WebappContextMapper) hostMapper).addHost(ctx.getHostname(), null); - ((WebappContextMapper) hostMapper).addContext(ctx.getHostname(), - ctx); - - contexts.add(ctx); - - getObjectManager().bind("ServletContext:" + ctx.getContextPath(), - ctx); - - } - - /** - * Add a context. - * - * web.xml will be read as part of init, and the initialization will be - * part of start or lazy. - * - * @param hostname - "" - * if default host, or string to be matched with Host header - * @param basePath = directory where the webapp is installed - * @param path - - * context path, "/" for root, "/examples", etc - * @return a servlet context - * @throws ServletException - */ - public ServletContext addServletContext(String hostname, - String basePath, - String path) - throws ServletException - { - ServletContextImpl ctx = new ServletContextImpl(); - ctx.setContextPath(path); - ctx.setBasePath(basePath); - addServletContext(ctx); - return ctx; - } - - public void removeServletContext(ServletContext sctx) - throws ServletException - { - ServletContextImpl ctx = (ServletContextImpl) sctx; - // TODO: destroy all servlets and filters - // TODO: clean up any other reference to the context or its loader - notifyRemove(ctx); - } - - - /** - * Required for ServletContext.getContext(uri); - * @throws ServletException - * @throws IOException - */ - public ServletContextImpl getContext(ServletContextImpl impl, String uri) - throws IOException, ServletException { - // Create a request - needs to be simplified - ServletRequestImpl req = createMessage(impl, impl.contextPath, uri); - hostMapper.doFilter(req, null, null); - return req.getContext(); - } - - public ServletResponseImpl service(ServletRequestImpl req) throws IOException, Exception { - ServletResponseImpl res = req.getResponse(); - service(req, res); - endRequest(req, res); - return res; - } - - public void service(ServletRequestImpl req, ServletResponseImpl res) - throws Exception, IOException { - // parse the session id from URI - req.parseSessionId(); - - try { - UriNormalizer.decodeRequest(req.getHttpRequest().decodedURI(), - req.getHttpRequest().requestURI(), - req.getHttpRequest().getURLDecoder()); - } catch(IOException ioe) { - res.setStatus(400); - return; - } - - MappingData mapRes = req.getMappingData(); - try { - // TODO: make hostMapper configurable, implement interface, - // simple to set on ROOT context - hostMapper.doFilter(req, null, null); - - - ServletContextImpl ctx = (ServletContextImpl)mapRes.context; - if( ctx == null ) { - // TODO: 404 - res.setStatus(404); - return; - } - req.setContext(ctx); - - // bind class loader - Thread.currentThread().setContextClassLoader(ctx.getClassLoader()); - - WebappServletMapper mapper = ctx.getMapper(); - mapper.map(req.getHttpRequest().decodedURI(), mapRes); - - // Possible redirect - MessageBytes redirectPathMB = mapRes.redirectPath; - if (!redirectPathMB.isNull()) { - String redirectPath = res.urlEncoder.encodeURL(redirectPathMB.toString()); - String query = req.getQueryString(); - if (req.isRequestedSessionIdFromURL()) { - // This is not optimal, but as this is not very common, it - // shouldn't matter - redirectPath = redirectPath + ";" + ServletRequestImpl.SESSION_PARAMETER_NAME + "=" - + req.getRequestedSessionId(); - } - if (query != null) { - // This is not optimal, but as this is not very common, it - // shouldn't matter - redirectPath = redirectPath + "?" + query; - } - res.sendRedirect(redirectPath); - return; - } - - req.parseSessionCookiesId(); - - ServletConfigImpl h=(ServletConfigImpl)mapRes.wrapper; - if (h != null) { - req.setWrapper((ServletConfigImpl)mapRes.wrapper); - serviceServlet(ctx, req, res, h, mapRes ); - // send the response... - - //res.flushBuffer(); - - // Recycle the wrapper request and response - //req.recycle(); - //res.recycle(); - } - } finally { - if(mapRes != null ) - mapRes.recycle(); - } - } - - /** Coyote / mapper adapter. Result of the mapper. - * - * This replaces the valve chain, the path is: - * 1. coyote calls mapper -> result Adapter - * 2. service is called. Additional filters are set on the wrapper. - * @param mapRes - */ - private void serviceServlet(ServletContextImpl ctx, - ServletRequestImpl req, - ServletResponseImpl res, - ServletConfigImpl servletConfig, - MappingData mapRes) - throws IOException { - Servlet servlet = null; - try { - if (servletConfig.isUnavailable()) { - handleUnavailable(res, servletConfig); - return; - } - try { - servlet = servletConfig.allocate(); - } catch(ServletException ex) { - handleUnavailable(res, servletConfig); - } - WebappFilterMapper filterMap = ctx.getFilterMapper(); - FilterChainImpl chain = - filterMap.createFilterChain(req, servletConfig, servlet); - - try { - if (chain == null) { - if (servlet != null) { - servlet.service(req, res); - } else { - System.err.println("No servlet " + req.getRequestURI()); - res.sendError(404); - } - } else { - chain.doFilter(req, res); - } - } catch(UnavailableException ex) { - servletConfig.unavailable(ex); - handleUnavailable(res, servletConfig); - return; - } - - // servlet completed without exception. Check status - int status = res.getStatus(); - if (status != 200 && !res.isCommitted()) { - String statusPage = ctx.findStatusPage(status); - - if (statusPage != null) { - ctx.handleStatusPage(req, res, status, statusPage); - } else { - // Send a default message body. - // TODO: maybe other mechanism to customize default. - res.defaultStatusPage(status, res.getMessage()); - } - } - } catch (Throwable t) { - ctx.handleError(req, res, t); - } finally { - if (servlet != null) { - servletConfig.deallocate(servlet); - } - } - } - - private void handleUnavailable(ServletResponseImpl response, - ServletConfigImpl servletConfig) - throws IOException { - long available = servletConfig.getAvailable(); - if ((available > 0L) && (available < Long.MAX_VALUE)) - response.setDateHeader("Retry-After", available); - // TODO: handle via error pages ! - response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, - "Service unavailable"); - } - - - // ------------ Notifications for JMX ---------------- - - void notifyAdd(Object o) { - } - - void notifyRemove(Object o) { - } - - public void setServerDir(String dir) { - this.serverDirName = dir; - } - - public File getWork() { - if (workDir == null) { - if (serverDirName == null) { - serverDirName = "./"; - } - File rootDirFile = new File(serverDirName); - workDir = new File(rootDirFile, "tomcat-work"); - if (workDir.exists()) { - workDir.mkdirs(); - } - } - return workDir; - } - - /** - * Init - * - * @throws ServletException - * @throws IOException - */ - public void init() throws ServletException, IOException { - getObjectManager().bind("TomcatLite", this); - if (contexts.size() == 0) { - setContext("/:./webapps/ROOT"); - } - getConnector().setObjectManager(getObjectManager()); - Iterator i1 = contexts.iterator(); - while (i1.hasNext()) { - ServletContextImpl ctx = (ServletContextImpl) i1.next(); - try { - ctx.init(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - - /** - * Initialize an webapp and add it to the server. - * - load web.xml - * - call - * - * @param rootDir - * @param path - * @param deployServlet - */ - public void init(String rootDir, String path) - throws ServletException, IOException { - - long t0 = System.currentTimeMillis(); - - ServletContextImpl ctx = - (ServletContextImpl)addServletContext(null, - rootDir, - path); - ctx.init(); - - long t1 = System.currentTimeMillis(); - - // At this point all config is loaded. Contexts are not yet init() - // - this will happen on start. - System.err.println("Context.init() " + path + " " + (t1-t0)); - } - - /** - * Get an empty request/response pair ( response available - * as getResponse() ). Optional set input and output buffers. - * - * This can be used for a connector-less interface to tomcat lite. - * - * TODO: make it independent of coyote ! - */ - - public ServletRequestImpl createMessage() { - ServletRequestImpl req = new ServletRequestImpl(); - ServletResponseImpl res = req.getResponse(); - - getConnector().initRequest(req, res); - - req.getHttpRequest().method().setString("GET"); - req.getHttpRequest().protocol().setString("HTTP/1.1"); - - return req; - } - - /** - * Used internally for mapping. - */ - private ServletRequestImpl createMessage(ServletContextImpl deployCtx, - String ctxPath, - String reqPath) { - ServletRequestImpl req = createMessage(); - req.setContextPath(ctxPath); - req.setContext(deployCtx); - req.setRequestURI(ctxPath + reqPath); - return req; - } - - - /** - * Set a global filter that will be used for context mapping. - * - * The filter will get a request, with requestUri and hostname set. - * - * It needs to compute the context path and set it as an attribute. - * - * Advanced features may include on-demand loading of webapps, large scale - * virtual hosting, etc. - */ - public void setContextMapper(Filter hostMapper2) { - this.hostMapper = hostMapper2; - } - - public void endRequest(ServletRequestImpl req, - ServletResponseImpl res) throws IOException { - res.outputBuffer.flush(); - req.getConnector().finishResponse(res); - } - - public Connector getConnector() { - if (connector == null) { - connector = (Connector) getObjectManager().get(Connector.class); - setConnector(connector); - } - return connector; - } - - public void setConnector(Connector c) { - connector = c; - connector.setTomcatLite(this); - getObjectManager().bind("Connector", connector); - } - - - public void setDaemon(boolean d) { - getConnector(); - if (connector != null) { - connector.setDaemon(d); - } - } - - public void startConnector() throws IOException { - getConnector(); - if (connector != null) { - connector.start(); - } - } - - public void stopConnector() throws Exception { - if (connector != null) { - connector.stop(); - } - } - - public void run() { - try { - execute(); - } catch (ServletException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public void execute() throws ServletException, IOException { - init(); - start(); - startConnector(); - } -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappContextMapper.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappContextMapper.java deleted file mode 100644 index ae0d9698e..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappContextMapper.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -import org.apache.tomcat.util.buf.CharChunk; -import org.apache.tomcat.util.buf.MessageBytes; -import org.apache.tomcat.util.http.mapper.MappingData; - -/** - * This handles host and context mapping. - * - * The default implementation for tomcat lite is very limitted - - * no support for virtual hosts, no support for contexts deeper than - * 1 level. The intention is to override this with more advanced mappers. - * - * With 'ConfigurableHosts' interface it is possible for a smart - * mapper to load/unload virtual hosts at runtime, maybe from a - * database. It should be possible to use databases to store huge number - * of hosts or webapps. - * - */ -public class WebappContextMapper implements Filter { - - ServletContext rootContext; - Map contexts = new HashMap(); - - public WebappContextMapper() { - } - - public void addHost(String name, String[] aliases) { - } - - /** - * Add a new Context to an existing Host. - * - * @param hostName Virtual host name this context belongs to - * @param contextPath Context path - * @param context Context object - * @param welcomeResources Welcome files defined for this context - * @param resources Static resources of the context - */ - public void addContext(String hostName, - ServletContext context) - throws ServletException - { - String path = context.getContextPath(); - if (path.lastIndexOf("/") > 0) { - throw new ServletException("Base context mapper supports only one level"); - } - if ("/".equals(path)) { - rootContext = context; - } - MessageBytes mb = MessageBytes.newInstance(); - mb.setChars(path.toCharArray(), 0, path.length()); - contexts.put(mb, context); - } - - - /** - * Remove a context from an existing host. - * - * @param hostName Virtual host name this context belongs to - * @param path Context path - */ - public void removeContext(String hostName, String path) - throws ServletException { - if ("/".equals(path)) { - rootContext = null; - } - contexts.remove(path); - } - - /** - * Map the specified URI. - */ - private void mapContext(ServletRequestImpl req) - throws IOException, ServletException { - MessageBytes uriMB = req.getDecodedRequestURIMB(); - MappingData mappingData = req.getMappingData(); - uriMB.toChars(); - CharChunk uri = uriMB.getCharChunk(); - - - if (uri.length() < 2 || contexts.size() == 0) { - mappingData.context = rootContext; - if (rootContext != null) { - mappingData.contextPath.setString(rootContext.getContextPath()); - } - return; - } - - int nextSlash = uri.indexOf('/', 1); - if (nextSlash == -1) { - nextSlash = uri.length(); - } - mappingData.contextPath.setChars(uri.getChars(), 0, nextSlash); - ServletContext servletContext = contexts.get(mappingData.contextPath); - - if (servletContext != null) { - mappingData.context = servletContext; - } else { - mappingData.context = rootContext; - if (rootContext != null) { - mappingData.contextPath.setString(rootContext.getContextPath()); - } - } - } - - public void init(FilterConfig filterConfig) throws ServletException { - } - - public void doFilter(ServletRequest request, - ServletResponse response, - FilterChain chain) - throws IOException, ServletException { - ServletRequestImpl req = (ServletRequestImpl)request; - mapContext(req); - } - - public void destroy() { - } -} \ No newline at end of file diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappFilterMapper.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappFilterMapper.java deleted file mode 100644 index f6cfaedad..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappFilterMapper.java +++ /dev/null @@ -1,533 +0,0 @@ -/* - * 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.tomcat.lite; - - -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.Servlet; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; - -import org.apache.tomcat.servlets.util.RequestUtil; - -/** - * First filter after the context and servlet are mapped. It will add - * web.xml-defined filters. - * - * costin: This is another mapping - done in RequestDispatcher or initial - * mapping. - * Also: StandardHostValve - sets attribute for error pages, - * StandardWrapperValve - mapping per invocation - * - * @author Greg Murray - * @author Remy Maucherat - */ -public class WebappFilterMapper implements Filter { - - - // -------------------------------------------------------------- Constants - - - public static final int ERROR = 1; - public static final Integer ERROR_INTEGER = new Integer(ERROR); - public static final int FORWARD = 2; - public static final Integer FORWARD_INTEGER = new Integer(FORWARD); - public static final int INCLUDE = 4; - public static final Integer INCLUDE_INTEGER = new Integer(INCLUDE); - public static final int REQUEST = 8; - public static final Integer REQUEST_INTEGER = new Integer(REQUEST); - - /** - * Request dispatcher state. - */ - public static final String DISPATCHER_TYPE_ATTR = - "org.apache.catalina.core.DISPATCHER_TYPE"; - - /** - * Request dispatcher path. - */ - public static final String DISPATCHER_REQUEST_PATH_ATTR = - "org.apache.catalina.core.DISPATCHER_REQUEST_PATH"; - - - // ----------------------------------------------------------- Constructors - ServletContextImpl servletContext; - - public WebappFilterMapper() { - } - - public WebappFilterMapper(ServletContextImpl impl) { - servletContext = impl; - } - - public void setServletContext(ServletContextImpl sc) { - servletContext = sc; - } - - // --------------------------------------------------------- Public Methods - - ArrayList filterMaps = new ArrayList(); - - public void addMapping(String filterName, - String url, - String servletName, - String type[], boolean isMatchAfter) { - FilterMap map = new FilterMap(); - map.setURLPattern(url); - map.setFilterName(filterName); - map.setServletName(servletName); - if (isMatchAfter) { - filterMaps.add(map); - } else { - filterMaps.add(0, map); - } - } - - /** - * Construct and return a FilterChain implementation that will wrap the - * execution of the specified servlet instance. If we should not execute - * a filter chain at all, return null. - * - * @param request The servlet request we are processing - * @param servlet The servlet instance to be wrapped - */ - public FilterChainImpl createFilterChain(ServletRequest request, - ServletConfigImpl wrapper, - Servlet servlet) { - - // If there is no servlet to execute, return null - if (servlet == null) - return (null); - - // get the dispatcher type - int dispatcher = -1; - if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) { - Integer dispatcherInt = - (Integer) request.getAttribute(DISPATCHER_TYPE_ATTR); - dispatcher = dispatcherInt.intValue(); - } - String requestPath = null; - Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR); - - if (attribute != null){ - requestPath = attribute.toString(); - } - - HttpServletRequest hreq = null; - if (request instanceof HttpServletRequest) - hreq = (HttpServletRequest)request; - - // Create and initialize a filter chain object - FilterChainImpl filterChain = null; - if ((request instanceof ServletRequestImpl)) { - ServletRequestImpl req = (ServletRequestImpl) request; - filterChain = (FilterChainImpl) req.getFilterChain(); - filterChain.release(); - } else { - // Security: Do not recycle - filterChain = new FilterChainImpl(); - } - - filterChain.setServlet(wrapper, servlet); - - // If there are no filter mappings, we are done - if ((filterMaps.size() == 0)) - return (filterChain); - - // Acquire the information we will need to match filter mappings - String servletName = wrapper.getServletName(); - - int n = 0; - - // TODO(costin): optimize: separate in 2 lists, one for url-mapped, one for - // servlet-name. Maybe even separate list for dispatcher and - // non-dispatcher - - // TODO(costin): optimize: set the FilterConfig in the FilterMap, to - // avoid second hash lookup - - // Add the relevant path-mapped filters to this filter chain - for (int i = 0; i < filterMaps.size(); i++) { - FilterMap filterMap = (FilterMap)filterMaps.get(i); - if (!matchDispatcher(filterMap ,dispatcher)) { - continue; - } - if (!matchFiltersURL(filterMap, requestPath)) - continue; - FilterConfigImpl filterConfig = - servletContext.getFilter(filterMap.getFilterName()); - if (filterConfig == null) { - // FIXME - log configuration problem - continue; - } - filterChain.addFilter(filterConfig); - n++; - } - - // Add filters that match on servlet name second - for (int i = 0; i < filterMaps.size(); i++) { - FilterMap filterMap = (FilterMap)filterMaps.get(i); - if (!matchDispatcher(filterMap ,dispatcher)) { - continue; - } - if (!matchFiltersServlet(filterMap, servletName)) - continue; - FilterConfigImpl filterConfig = - servletContext.getFilter(filterMap.getFilterName()); - if (filterConfig == null) { - ; // FIXME - log configuration problem - continue; - } - filterChain.addFilter(filterConfig); - n++; - } - - // Return the completed filter chain - return (filterChain); - - } - - - // -------------------------------------------------------- Private Methods - - - /** - * Return true if the context-relative request path - * matches the requirements of the specified filter mapping; - * otherwise, return null. - * - * @param filterMap Filter mapping being checked - * @param requestPath Context-relative request path of this request - */ - private boolean matchFiltersURL(FilterMap filterMap, String requestPath) { - - if (requestPath == null) - return (false); - - // Match on context relative request path - String testPath = filterMap.getURLPattern(); - if (testPath == null) - return (false); - - // Case 1 - Exact Match - if (testPath.equals(requestPath)) - return (true); - - // Case 2 - Path Match ("/.../*") - if (testPath.equals("/*")) - return (true); - if (testPath.endsWith("/*")) { - if (testPath.regionMatches(0, requestPath, 0, - testPath.length() - 2)) { - if (requestPath.length() == (testPath.length() - 2)) { - return (true); - } else if ('/' == requestPath.charAt(testPath.length() - 2)) { - return (true); - } - } - return (false); - } - - // Case 3 - Extension Match - if (testPath.startsWith("*.")) { - int slash = requestPath.lastIndexOf('/'); - int period = requestPath.lastIndexOf('.'); - if ((slash >= 0) && (period > slash) - && (period != requestPath.length() - 1) - && ((requestPath.length() - period) - == (testPath.length() - 1))) { - return (testPath.regionMatches(2, requestPath, period + 1, - testPath.length() - 2)); - } - } - - // Case 4 - "Default" Match - return (false); // NOTE - Not relevant for selecting filters - - } - - - /** - * Return true if the specified servlet name matches - * the requirements of the specified filter mapping; otherwise - * return false. - * - * @param filterMap Filter mapping being checked - * @param servletName Servlet name being checked - */ - private boolean matchFiltersServlet(FilterMap filterMap, - String servletName) { - - if (servletName == null) { - return (false); - } else { - if (servletName.equals(filterMap.getServletName())) { - return (true); - } else { - return false; - } - } - - } - - - /** - * Convienience method which returns true if the dispatcher type - * matches the dispatcher types specified in the FilterMap - */ - private boolean matchDispatcher(FilterMap filterMap, int dispatcher) { - switch (dispatcher) { - case FORWARD : { - if (filterMap.getDispatcherMapping() == FilterMap.FORWARD || - filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR || - filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) { - return true; - } - break; - } - case INCLUDE : { - if (filterMap.getDispatcherMapping() == FilterMap.INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR || - filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) { - return true; - } - break; - } - case REQUEST : { - if (filterMap.getDispatcherMapping() == FilterMap.REQUEST || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE) { - return true; - } - break; - } - case ERROR : { - if (filterMap.getDispatcherMapping() == FilterMap.ERROR || - filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR || - filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR || - filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE || - filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE) { - return true; - } - break; - } - } - return false; - } - - - // -------------------- Map elements ----------------------- - - public static class FilterMap implements Serializable { - - - // ------------------------------------------------------------- Properties - - - /** - * The name of this filter to be executed when this mapping matches - * a particular request. - */ - - public static final int ERROR = 1; - public static final int FORWARD = 2; - public static final int FORWARD_ERROR =3; - public static final int INCLUDE = 4; - public static final int INCLUDE_ERROR = 5; - public static final int INCLUDE_ERROR_FORWARD =6; - public static final int INCLUDE_FORWARD = 7; - public static final int REQUEST = 8; - public static final int REQUEST_ERROR = 9; - public static final int REQUEST_ERROR_FORWARD = 10; - public static final int REQUEST_ERROR_FORWARD_INCLUDE = 11; - public static final int REQUEST_ERROR_INCLUDE = 12; - public static final int REQUEST_FORWARD = 13; - public static final int REQUEST_INCLUDE = 14; - public static final int REQUEST_FORWARD_INCLUDE= 15; - - // represents nothing having been set. This will be seen - // as equal to a REQUEST - private static final int NOT_SET = -1; - - private int dispatcherMapping=NOT_SET; - - private String filterName = null; - - /** - * The URL pattern this mapping matches. - */ - private String urlPattern = null; - - /** - * The servlet name this mapping matches. - */ - private String servletName = null; - - - - public String getFilterName() { - return (this.filterName); - } - - public void setFilterName(String filterName) { - this.filterName = filterName; - } - - - public String getServletName() { - return (this.servletName); - } - - public void setServletName(String servletName) { - this.servletName = servletName; - } - - - public String getURLPattern() { - return (this.urlPattern); - } - - public void setURLPattern(String urlPattern) { - this.urlPattern = RequestUtil.URLDecode(urlPattern); - } - - /** - * - * This method will be used to set the current state of the FilterMap - * representing the state of when filters should be applied: - * - * ERROR - * FORWARD - * FORWARD_ERROR - * INCLUDE - * INCLUDE_ERROR - * INCLUDE_ERROR_FORWARD - * REQUEST - * REQUEST_ERROR - * REQUEST_ERROR_INCLUDE - * REQUEST_ERROR_FORWARD_INCLUDE - * REQUEST_INCLUDE - * REQUEST_FORWARD, - * REQUEST_FORWARD_INCLUDE - * - */ - public void setDispatcher(String dispatcherString) { - String dispatcher = dispatcherString.toUpperCase(); - - if (dispatcher.equals("FORWARD")) { - - // apply FORWARD to the global dispatcherMapping. - switch (dispatcherMapping) { - case NOT_SET : dispatcherMapping = FORWARD; break; - case ERROR : dispatcherMapping = FORWARD_ERROR; break; - case INCLUDE : dispatcherMapping = INCLUDE_FORWARD; break; - case INCLUDE_ERROR : dispatcherMapping = INCLUDE_ERROR_FORWARD; break; - case REQUEST : dispatcherMapping = REQUEST_FORWARD; break; - case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_FORWARD; break; - case REQUEST_ERROR_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; - case REQUEST_INCLUDE : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break; - } - } else if (dispatcher.equals("INCLUDE")) { - // apply INCLUDE to the global dispatcherMapping. - switch (dispatcherMapping) { - case NOT_SET : dispatcherMapping = INCLUDE; break; - case ERROR : dispatcherMapping = INCLUDE_ERROR; break; - case FORWARD : dispatcherMapping = INCLUDE_FORWARD; break; - case FORWARD_ERROR : dispatcherMapping = INCLUDE_ERROR_FORWARD; break; - case REQUEST : dispatcherMapping = REQUEST_INCLUDE; break; - case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_INCLUDE; break; - case REQUEST_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; - case REQUEST_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break; - } - } else if (dispatcher.equals("REQUEST")) { - // apply REQUEST to the global dispatcherMapping. - switch (dispatcherMapping) { - case NOT_SET : dispatcherMapping = REQUEST; break; - case ERROR : dispatcherMapping = REQUEST_ERROR; break; - case FORWARD : dispatcherMapping = REQUEST_FORWARD; break; - case FORWARD_ERROR : dispatcherMapping = REQUEST_ERROR_FORWARD; break; - case INCLUDE : dispatcherMapping = REQUEST_INCLUDE; break; - case INCLUDE_ERROR : dispatcherMapping = REQUEST_ERROR_INCLUDE; break; - case INCLUDE_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break; - case INCLUDE_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; - } - } else if (dispatcher.equals("ERROR")) { - // apply ERROR to the global dispatcherMapping. - switch (dispatcherMapping) { - case NOT_SET : dispatcherMapping = ERROR; break; - case FORWARD : dispatcherMapping = FORWARD_ERROR; break; - case INCLUDE : dispatcherMapping = INCLUDE_ERROR; break; - case INCLUDE_FORWARD : dispatcherMapping = INCLUDE_ERROR_FORWARD; break; - case REQUEST : dispatcherMapping = REQUEST_ERROR; break; - case REQUEST_INCLUDE : dispatcherMapping = REQUEST_ERROR_INCLUDE; break; - case REQUEST_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD; break; - case REQUEST_FORWARD_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break; - } - } - } - - public int getDispatcherMapping() { - // per the SRV.6.2.5 absence of any dispatcher elements is - // equivelant to a REQUEST value - if (dispatcherMapping == NOT_SET) return REQUEST; - else return dispatcherMapping; - } - - } - - - public void init(FilterConfig filterConfig) throws ServletException { - } - - - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) - throws IOException, ServletException { - } - - - public void destroy() { - } - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappServletMapper.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappServletMapper.java deleted file mode 100644 index a6eb28438..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/WebappServletMapper.java +++ /dev/null @@ -1,883 +0,0 @@ -/* - * 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.tomcat.lite; - -import java.io.File; -import java.io.IOException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -import org.apache.tomcat.util.buf.Ascii; -import org.apache.tomcat.util.buf.CharChunk; -import org.apache.tomcat.util.buf.MessageBytes; -import org.apache.tomcat.util.http.mapper.MappingData; - -/** - * Mapper, which implements the servlet API mapping rules (which are derived - * from the HTTP rules). - * - * Based on catalina mapper - but simplified. All host and context mappings - * is done in HostMapper - this is just dealing with web.xml. - * - * For corner cases ( very large number of rules, dynamic rules, etc ) you - * can override the mapper for a context with a class extending this. - * - * TODO: remove, use coyote-level mapper or user-space - */ -public class WebappServletMapper implements Filter { - - /** - * Context associated with this wrapper, used for wrapper mapping. - */ - public ContextMapElement contextMapElement = new ContextMapElement(); - - - // --------------------------------------------------------- Public Methods - public WebappServletMapper() { - } - - public void setServletContext(ServletContextImpl impl) { - contextMapElement.object = impl; - contextMapElement.name = impl.getContextPath(); - } - - - /** Set context, used for wrapper mapping (request dispatcher). - * - * @param welcomeResources Welcome files defined for this context - * @param resources Static resources of the context - */ - public void setContext(String path, String[] welcomeResources, - File resources) { - contextMapElement.name = path; - contextMapElement.welcomeResources = welcomeResources; - contextMapElement.resources = resources; - } - - - /** - * Add a wrapper to the context associated with this wrapper. - * - * @param path Wrapper mapping - * @param wrapper The Wrapper object - */ - public void addWrapper(String path, Object wrapper) { - addWrapper(contextMapElement, path, wrapper); - } - - - public void addWrapper(String path, Object wrapper, boolean jspWildCard) { - addWrapper(contextMapElement, path, wrapper, jspWildCard); - } - - - public void addWrapper(ContextMapElement context, String path, Object wrapper) { - addWrapper(context, path, wrapper, false); - } - - - /** - * Adds a wrapper to the given context. - * - * @param context The context to which to add the wrapper - * @param path Wrapper mapping - * @param wrapper The Wrapper object - * @param jspWildCard true if the wrapper corresponds to the JspServlet - * and the mapping path contains a wildcard; false otherwise - */ - protected void addWrapper(ContextMapElement context, String path, Object wrapper, - boolean jspWildCard) { - - synchronized (context) { - WrapperMapElement newWrapper = new WrapperMapElement(); - newWrapper.object = wrapper; - newWrapper.jspWildCard = jspWildCard; - if (path.endsWith("/*")) { - // Wildcard wrapper - newWrapper.name = path.substring(0, path.length() - 2); - WrapperMapElement[] oldWrappers = context.wildcardWrappers; - WrapperMapElement[] newWrappers = - new WrapperMapElement[oldWrappers.length + 1]; - if (insertMap(oldWrappers, newWrappers, newWrapper)) { - context.wildcardWrappers = newWrappers; - int slashCount = slashCount(newWrapper.name); - if (slashCount > context.nesting) { - context.nesting = slashCount; - } - } - } else if (path.startsWith("*.")) { - // Extension wrapper - newWrapper.name = path.substring(2); - WrapperMapElement[] oldWrappers = context.extensionWrappers; - WrapperMapElement[] newWrappers = - new WrapperMapElement[oldWrappers.length + 1]; - if (insertMap(oldWrappers, newWrappers, newWrapper)) { - context.extensionWrappers = newWrappers; - } - } else if (path.equals("/")) { - // Default wrapper - newWrapper.name = ""; - context.defaultWrapper = newWrapper; - } else { - // Exact wrapper - newWrapper.name = path; - WrapperMapElement[] oldWrappers = context.exactWrappers; - WrapperMapElement[] newWrappers = - new WrapperMapElement[oldWrappers.length + 1]; - if (insertMap(oldWrappers, newWrappers, newWrapper)) { - context.exactWrappers = newWrappers; - } - } - } - } - - - /** - * Remove a wrapper from the context associated with this wrapper. - * - * @param path Wrapper mapping - */ - public void removeWrapper(String path) { - removeWrapper(contextMapElement, path); - } - - - protected void removeWrapper(ContextMapElement context, String path) { - synchronized (context) { - if (path.endsWith("/*")) { - // Wildcard wrapper - String name = path.substring(0, path.length() - 2); - WrapperMapElement[] oldWrappers = context.wildcardWrappers; - WrapperMapElement[] newWrappers = - new WrapperMapElement[oldWrappers.length - 1]; - if (removeMap(oldWrappers, newWrappers, name)) { - // Recalculate nesting - context.nesting = 0; - for (int i = 0; i < newWrappers.length; i++) { - int slashCount = slashCount(newWrappers[i].name); - if (slashCount > context.nesting) { - context.nesting = slashCount; - } - } - context.wildcardWrappers = newWrappers; - } - } else if (path.startsWith("*.")) { - // Extension wrapper - String name = path.substring(2); - WrapperMapElement[] oldWrappers = context.extensionWrappers; - WrapperMapElement[] newWrappers = - new WrapperMapElement[oldWrappers.length - 1]; - if (removeMap(oldWrappers, newWrappers, name)) { - context.extensionWrappers = newWrappers; - } - } else if (path.equals("/")) { - // Default wrapper - context.defaultWrapper = null; - } else { - // Exact wrapper - String name = path; - WrapperMapElement[] oldWrappers = context.exactWrappers; - WrapperMapElement[] newWrappers = - new WrapperMapElement[oldWrappers.length - 1]; - if (removeMap(oldWrappers, newWrappers, name)) { - context.exactWrappers = newWrappers; - } - } - } - } - - /** - * Map the specified URI relative to the context, - * mutating the given mapping data. - * - * @param uri URI - * @param mappingData This structure will contain the result of the mapping - * operation - */ - public void map(MessageBytes uri, MappingData mappingData) - throws Exception { - - uri.toChars(); - CharChunk uricc = uri.getCharChunk(); - //uricc.setLimit(-1); - internalMapWrapper(contextMapElement, uricc, mappingData); - - } - - - // -------------------------------------------------------- Private Methods - - - /** - * Wrapper mapping. - */ - private final void internalMapWrapper(ContextMapElement context, - CharChunk path, - MappingData mappingData) - throws Exception { - - int pathOffset = path.getOffset(); - int pathEnd = path.getEnd(); - int servletPath = pathOffset; - boolean noServletPath = false; - - int length = context.name.length(); - if (length == 1) length--; - if (length != (pathEnd - pathOffset)) { - servletPath = pathOffset + length; - } else { - noServletPath = true; - // What is this doing ??? - path.append('/'); - pathOffset = path.getOffset(); - pathEnd = path.getEnd(); - servletPath = pathOffset+length; - } - - path.setOffset(servletPath); - - // Rule 1 -- Exact Match - WrapperMapElement[] exactWrappers = context.exactWrappers; - internalMapExactWrapper(exactWrappers, path, mappingData); - - // Rule 2 -- Prefix Match - boolean checkJspWelcomeFiles = false; - WrapperMapElement[] wildcardWrappers = context.wildcardWrappers; - if (mappingData.wrapper == null) { - internalMapWildcardWrapper(wildcardWrappers, context.nesting, - path, mappingData); - if (mappingData.wrapper != null && mappingData.jspWildCard) { - char[] buf = path.getBuffer(); - if (buf[pathEnd - 1] == '/') { - /* - * Path ending in '/' was mapped to JSP servlet based on - * wildcard match (e.g., as specified in url-pattern of a - * jsp-property-group. - * Force the context's welcome files, which are interpreted - * as JSP files (since they match the url-pattern), to be - * considered. See Bugzilla 27664. - */ - mappingData.wrapper = null; - checkJspWelcomeFiles = true; - } else { - // See Bugzilla 27704 - mappingData.wrapperPath.setChars(buf, path.getStart(), - path.getLength()); - mappingData.pathInfo.recycle(); - } - } - } - - if(mappingData.wrapper == null && noServletPath) { - // The path is empty, redirect to "/" - mappingData.redirectPath.setChars - (path.getBuffer(), pathOffset, pathEnd); - path.setEnd(pathEnd - 1); - return; - } - - // Rule 3 -- Extension Match - WrapperMapElement[] extensionWrappers = context.extensionWrappers; - if (mappingData.wrapper == null && !checkJspWelcomeFiles) { - internalMapExtensionWrapper(extensionWrappers, path, mappingData); - } - - File file = null; - // Rule 4 -- Welcome resources processing for servlets - if (mappingData.wrapper == null) { - boolean checkWelcomeFiles = checkJspWelcomeFiles; - if (!checkWelcomeFiles) { - char[] buf = path.getBuffer(); - checkWelcomeFiles = (buf[pathEnd - 1] == '/'); - } - if (checkWelcomeFiles) { - for (int i = 0; (i < context.welcomeResources.length) - && (mappingData.wrapper == null); i++) { - path.setOffset(pathOffset); - path.setEnd(pathEnd); - path.append(context.welcomeResources[i], 0, - context.welcomeResources[i].length()); - path.setOffset(servletPath); - - // Rule 4a -- Welcome resources processing for exact macth - internalMapExactWrapper(exactWrappers, path, mappingData); - - // Rule 4b -- Welcome resources processing for prefix match - if (mappingData.wrapper == null) { - internalMapWildcardWrapper - (wildcardWrappers, context.nesting, - path, mappingData); - } - - // Rule 4c -- Welcome resources processing - // for physical folder - if (mappingData.wrapper == null - && context.resources != null) { - // Default servlet: check if it's file or dir to apply - // welcome files rules. - // TODO: Save the File in attributes, - // to avoid duplication in DefaultServlet. - - String pathStr = path.toString(); - file = new File(context.resources, pathStr); - if (file.exists() && !(file.isDirectory()) ) { - - internalMapExtensionWrapper(extensionWrappers, - path, mappingData); - if (mappingData.wrapper == null - && context.defaultWrapper != null) { - mappingData.wrapper = - context.defaultWrapper.object; - mappingData.requestPath.setChars - (path.getBuffer(), path.getStart(), - path.getLength()); - mappingData.wrapperPath.setChars - (path.getBuffer(), path.getStart(), - path.getLength()); - mappingData.requestPath.setString(pathStr); - mappingData.wrapperPath.setString(pathStr); - } - } - } - } - - path.setOffset(servletPath); - path.setEnd(pathEnd); - } - - } - - - // Rule 7 -- Default servlet - if (mappingData.wrapper == null && !checkJspWelcomeFiles) { - if (context.defaultWrapper != null) { - mappingData.wrapper = context.defaultWrapper.object; - mappingData.requestPath.setChars - (path.getBuffer(), path.getStart(), path.getLength()); - mappingData.wrapperPath.setChars - (path.getBuffer(), path.getStart(), path.getLength()); - } - // Redirection to a folder - char[] buf = path.getBuffer(); - if (context.resources != null && buf[pathEnd -1 ] != '/') { - String pathStr = path.toString(); - file = new File( context.resources, pathStr); - if (file.exists() && file.isDirectory()) { - // Note: this mutates the path: do not do any processing - // after this (since we set the redirectPath, there - // shouldn't be any) - path.setOffset(pathOffset); - path.append('/'); - mappingData.redirectPath.setChars - (path.getBuffer(), path.getStart(), path.getLength()); - } else { - mappingData.requestPath.setString(pathStr); - mappingData.wrapperPath.setString(pathStr); - } - } - } - - path.setOffset(pathOffset); - path.setEnd(pathEnd); - } - - - /** - * Exact mapping. - */ - private final void internalMapExactWrapper - (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) { - int pos = find(wrappers, path); - if ((pos != -1) && (path.equals(wrappers[pos].name))) { - mappingData.requestPath.setString(wrappers[pos].name); - mappingData.wrapperPath.setString(wrappers[pos].name); - mappingData.wrapper = wrappers[pos].object; - } - } - - - /** - * Wildcard mapping. - */ - private final void internalMapWildcardWrapper - (WrapperMapElement[] wrappers, int nesting, CharChunk path, - MappingData mappingData) { - - int pathEnd = path.getEnd(); - int pathOffset = path.getOffset(); - - int lastSlash = -1; - int length = -1; - int pos = find(wrappers, path); - if (pos != -1) { - boolean found = false; - while (pos >= 0) { - if (path.startsWith(wrappers[pos].name)) { - length = wrappers[pos].name.length(); - if (path.getLength() == length) { - found = true; - break; - } else if (path.startsWithIgnoreCase("/", length)) { - found = true; - break; - } - } - if (lastSlash == -1) { - lastSlash = nthSlash(path, nesting + 1); - } else { - lastSlash = lastSlash(path); - } - path.setEnd(lastSlash); - pos = find(wrappers, path); - } - path.setEnd(pathEnd); - if (found) { - mappingData.wrapperPath.setString(wrappers[pos].name); - if (path.getLength() > length) { - mappingData.pathInfo.setChars - (path.getBuffer(), - path.getOffset() + length, - path.getLength() - length); - } - mappingData.requestPath.setChars - (path.getBuffer(), path.getOffset(), path.getLength()); - mappingData.wrapper = wrappers[pos].object; - mappingData.jspWildCard = wrappers[pos].jspWildCard; - } - } - } - - - /** - * Extension mappings. - */ - private final void internalMapExtensionWrapper - (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) { - char[] buf = path.getBuffer(); - int pathEnd = path.getEnd(); - int servletPath = path.getOffset(); - int slash = -1; - for (int i = pathEnd - 1; i >= servletPath; i--) { - if (buf[i] == '/') { - slash = i; - break; - } - } - if (slash == -1 ) slash = 0; - if (slash >= 0) { - int period = -1; - for (int i = pathEnd - 1; i > slash; i--) { - if (buf[i] == '.') { - period = i; - break; - } - } - if (period >= 0) { - path.setOffset(period + 1); - path.setEnd(pathEnd); - int pos = find(wrappers, path); - if ((pos != -1) - && (path.equals(wrappers[pos].name))) { - mappingData.wrapperPath.setChars - (buf, servletPath, pathEnd - servletPath); - mappingData.requestPath.setChars - (buf, servletPath, pathEnd - servletPath); - mappingData.wrapper = wrappers[pos].object; - } - path.setOffset(servletPath); - path.setEnd(pathEnd); - } - } - } - - - /** - * Find a map elemnt given its name in a sorted array of map elements. - * This will return the index for the closest inferior or equal item in the - * given array. - */ - public static final int find(MapElement[] map, CharChunk name) { - return find(map, name, name.getStart(), name.getEnd()); - } - - - /** - * Find a map elemnt given its name in a sorted array of map elements. - * This will return the index for the closest inferior or equal item in the - * given array. - */ - private static final int find(MapElement[] map, CharChunk name, - int start, int end) { - - int a = 0; - int b = map.length - 1; - - // Special cases: -1 and 0 - if (b == -1) { - return -1; - } - - if (compare(name, start, end, map[0].name) < 0 ) { - return -1; - } - if (b == 0) { - return 0; - } - - int i = 0; - while (true) { - i = (b + a) / 2; - int result = compare(name, start, end, map[i].name); - if (result == 1) { - a = i; - } else if (result == 0) { - return i; - } else { - b = i; - } - if ((b - a) == 1) { - int result2 = compare(name, start, end, map[b].name); - if (result2 < 0) { - return a; - } else { - return b; - } - } - } - - } - - /** - * Find a map elemnt given its name in a sorted array of map elements. - * This will return the index for the closest inferior or equal item in the - * given array. - */ - private static final int findIgnoreCase(MapElement[] map, CharChunk name) { - return findIgnoreCase(map, name, name.getStart(), name.getEnd()); - } - - - /** - * Find a map elemnt given its name in a sorted array of map elements. - * This will return the index for the closest inferior or equal item in the - * given array. - */ - private static final int findIgnoreCase(MapElement[] map, CharChunk name, - int start, int end) { - - int a = 0; - int b = map.length - 1; - - // Special cases: -1 and 0 - if (b == -1) { - return -1; - } - if (compareIgnoreCase(name, start, end, map[0].name) < 0 ) { - return -1; - } - if (b == 0) { - return 0; - } - - int i = 0; - while (true) { - i = (b + a) / 2; - int result = compareIgnoreCase(name, start, end, map[i].name); - if (result == 1) { - a = i; - } else if (result == 0) { - return i; - } else { - b = i; - } - if ((b - a) == 1) { - int result2 = compareIgnoreCase(name, start, end, map[b].name); - if (result2 < 0) { - return a; - } else { - return b; - } - } - } - - } - - - /** - * Find a map elemnt given its name in a sorted array of map elements. - * This will return the index for the closest inferior or equal item in the - * given array. - */ - public static final int find(MapElement[] map, String name) { - - int a = 0; - int b = map.length - 1; - - // Special cases: -1 and 0 - if (b == -1) { - return -1; - } - - if (name.compareTo(map[0].name) < 0) { - return -1; - } - if (b == 0) { - return 0; - } - - int i = 0; - while (true) { - i = (b + a) / 2; - int result = name.compareTo(map[i].name); - if (result > 0) { - a = i; - } else if (result == 0) { - return i; - } else { - b = i; - } - if ((b - a) == 1) { - int result2 = name.compareTo(map[b].name); - if (result2 < 0) { - return a; - } else { - return b; - } - } - } - - } - - - /** - * Compare given char chunk with String. - * Return -1, 0 or +1 if inferior, equal, or superior to the String. - */ - private static final int compare(CharChunk name, int start, int end, - String compareTo) { - int result = 0; - char[] c = name.getBuffer(); - int len = compareTo.length(); - if ((end - start) < len) { - len = end - start; - } - for (int i = 0; (i < len) && (result == 0); i++) { - if (c[i + start] > compareTo.charAt(i)) { - result = 1; - } else if (c[i + start] < compareTo.charAt(i)) { - result = -1; - } - } - if (result == 0) { - if (compareTo.length() > (end - start)) { - result = -1; - } else if (compareTo.length() < (end - start)) { - result = 1; - } - } - return result; - } - - - /** - * Compare given char chunk with String ignoring case. - * Return -1, 0 or +1 if inferior, equal, or superior to the String. - */ - private static final int compareIgnoreCase(CharChunk name, int start, int end, - String compareTo) { - int result = 0; - char[] c = name.getBuffer(); - int len = compareTo.length(); - if ((end - start) < len) { - len = end - start; - } - for (int i = 0; (i < len) && (result == 0); i++) { - if (Ascii.toLower(c[i + start]) > Ascii.toLower(compareTo.charAt(i))) { - result = 1; - } else if (Ascii.toLower(c[i + start]) < Ascii.toLower(compareTo.charAt(i))) { - result = -1; - } - } - if (result == 0) { - if (compareTo.length() > (end - start)) { - result = -1; - } else if (compareTo.length() < (end - start)) { - result = 1; - } - } - return result; - } - - - /** - * Find the position of the last slash in the given char chunk. - */ - public static final int lastSlash(CharChunk name) { - - char[] c = name.getBuffer(); - int end = name.getEnd(); - int start = name.getStart(); - int pos = end; - - while (pos > start) { - if (c[--pos] == '/') { - break; - } - } - - return (pos); - - } - - - /** - * Find the position of the nth slash, in the given char chunk. - */ - public static final int nthSlash(CharChunk name, int n) { - - char[] c = name.getBuffer(); - int end = name.getEnd(); - int start = name.getStart(); - int pos = start; - int count = 0; - - while (pos < end) { - if ((c[pos++] == '/') && ((++count) == n)) { - pos--; - break; - } - } - - return (pos); - - } - - - /** - * Return the slash count in a given string. - */ - public static final int slashCount(String name) { - int pos = -1; - int count = 0; - while ((pos = name.indexOf('/', pos + 1)) != -1) { - count++; - } - return count; - } - - - /** - * Insert into the right place in a sorted MapElement array, and prevent - * duplicates. - */ - public static final boolean insertMap - (MapElement[] oldMap, MapElement[] newMap, MapElement newElement) { - int pos = find(oldMap, newElement.name); - if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) { - return false; - } - System.arraycopy(oldMap, 0, newMap, 0, pos + 1); - newMap[pos + 1] = newElement; - System.arraycopy - (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1); - return true; - } - - - /** - * Insert into the right place in a sorted MapElement array. - */ - public static final boolean removeMap - (MapElement[] oldMap, MapElement[] newMap, String name) { - int pos = find(oldMap, name); - if ((pos != -1) && (name.equals(oldMap[pos].name))) { - System.arraycopy(oldMap, 0, newMap, 0, pos); - System.arraycopy(oldMap, pos + 1, newMap, pos, - oldMap.length - pos - 1); - return true; - } - return false; - } - - - // ------------------------------------------------- MapElement Inner Class - - - protected static abstract class MapElement { - /** hostname or path - */ - public String name = null; - public Object object = null; - - public String toString() { - return "MapElement: \"" + name +"\""; - } - } - - - // ---------------------------------------------------- Context Inner Class - - - public static final class ContextMapElement - extends MapElement { - - public String[] welcomeResources = new String[0]; - public File resources = null; - public WrapperMapElement defaultWrapper = null; - public WrapperMapElement[] exactWrappers = new WrapperMapElement[0]; - public WrapperMapElement[] wildcardWrappers = new WrapperMapElement[0]; - public WrapperMapElement[] extensionWrappers = new WrapperMapElement[0]; - public int nesting = 0; - - public String toString() { - return "ContextMapElement {" + - "name: \"" + name + - "\"\nnesting: \"" + nesting + - "\"\n}"; - } - } - - - // ---------------------------------------------------- Wrapper Inner Class - - - public static class WrapperMapElement - extends MapElement { - public boolean jspWildCard = false; - } - - - public void destroy() { - } - - - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, - ServletException { - } - - - public void init(FilterConfig filterConfig) throws ServletException { - } - - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/config.properties b/modules/tomcat-lite/java/org/apache/tomcat/lite/config.properties deleted file mode 100644 index c1ef53d41..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/config.properties +++ /dev/null @@ -1,39 +0,0 @@ -# Support for PropertiesSpi - the 'dummy' framework used for tomcat-lite -# If tomcat is used with a proper framework, you need to bind and configure -# those objects in the framework. - -Main.(class)=org.apache.tomcat.lite.TomcatLite - -Jmx.(class)=org.apache.tomcat.integration.jmx.JmxObjectManagerSpi - -# --- Class names for required plugin interfaces --- - -org.apache.tomcat.lite.WebappServletMapper.(class)=org.apache.tomcat.lite.WebappServletMapper -org.apache.tomcat.lite.WebappFilterMapper.(class)=org.apache.tomcat.lite.WebappFilterMapper - -# Sessions -org.apache.tomcat.addons.UserSessionManager.(class)=org.apache.tomcat.servlets.session.SimpleSessionManager - -# *.jsp support -org.apache.tomcat.addons.UserTemplateClassMapper.(class)=org.apache.tomcat.servlets.jsp.JasperCompilerTemplateClassMapper - -org.apache.tomcat.addons.Filesystem.(class)=org.apache.tomcat.integration.simple.LocalFilesystem - -# Loader for web.xml - you can have your own custom class using a more efficient -# or hardcoded. -org.apache.tomcat.lite.ContextPreinitListener.(class)=org.apache.tomcat.lite.webxml.TomcatLiteWebXmlConfig - -# Connector class -org.apache.tomcat.lite.Connector.(class)=org.apache.tomcat.lite.coyote.CoyoteConnector - -# JMX -jmx-connector.(class)=org.apache.tomcat.integration.jmx.JmxObjectManagerSpi - -# --- Other required settings --- - -# Customize default and *.jsp mappings -default-servlet.(class)=org.apache.tomcat.servlets.file.WebdavServlet -jspwildcard-servlet.(class)=org.apache.tomcat.servlets.jsp.WildcardTemplateServlet -filetemplate-servlet.(class)=org.apache.tomcat.servlets.jsp.JspFileTemplateServlet - - diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/coyote/CoyoteConnector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/coyote/CoyoteConnector.java deleted file mode 100644 index 1a69269c8..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/coyote/CoyoteConnector.java +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite.coyote; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.coyote.ActionCode; -import org.apache.coyote.ActionHook; -import org.apache.coyote.Adapter; -import org.apache.coyote.ProtocolHandler; -import org.apache.coyote.Request; -import org.apache.coyote.Response; -import org.apache.coyote.http11.Http11NioProtocol; -import org.apache.tomcat.integration.ObjectManager; -import org.apache.tomcat.lite.BodyReader; -import org.apache.tomcat.lite.BodyWriter; -import org.apache.tomcat.lite.ClientAbortException; -import org.apache.tomcat.lite.Connector; -import org.apache.tomcat.lite.ServletRequestImpl; -import org.apache.tomcat.lite.ServletResponseImpl; -import org.apache.tomcat.lite.TomcatLite; -import org.apache.tomcat.util.buf.ByteChunk; -import org.apache.tomcat.util.buf.UriNormalizer; -import org.apache.tomcat.util.http.HttpRequest; -import org.apache.tomcat.util.http.HttpResponse; -import org.apache.tomcat.util.net.SocketStatus; - -public class CoyoteConnector implements Adapter, Connector { - - private TomcatLite lite; - - public CoyoteConnector() { - } - - - - public void acknowledge(HttpServletResponse res) throws IOException { - Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; - cres.acknowledge(); - } - - public void reset(HttpServletResponse res) { - Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; - cres.reset(); - } - - public void recycle(HttpServletRequest req, HttpServletResponse res) { - - } - - public static HttpResponse getResponse(final Response cres) { - HttpResponse hres = new HttpResponse() { - public int getStatus() { - return cres.getStatus(); - } - public void setStatus(int i) { - super.setStatus(i); - cres.setStatus(i); - } - public void setMessage(String s) { - super.setMessage(s); - cres.setMessage(s); - } - public String getMessage() { - return cres.getMessage(); - } - public boolean isCommitted() { - return cres.isCommitted(); - } - - public void setCommitted(boolean b) { - cres.setCommitted(b); - } - }; - - hres.setMimeHeaders(cres.getMimeHeaders()); - hres.nativeResponse = cres; - - return hres; - } - - public static HttpRequest getRequest(Request req) { - - HttpRequest httpReq = new HttpRequest(req.scheme(), - req.method(), - req.unparsedURI(), - req.protocol(), - req.getMimeHeaders(), - req.requestURI(), - req.decodedURI(), - req.query(), req.getParameters(), - req.serverName(), - req.getCookies()) { - - }; - httpReq.nativeRequest = req; - - // TODO: anything else computed in coyote ? - - return httpReq; - } - - @Override - public void initRequest(HttpServletRequest hreq, HttpServletResponse hres) { - ServletRequestImpl req = (ServletRequestImpl) hreq; - ServletResponseImpl res = (ServletResponseImpl) hres; - req.setConnector(this); - - Request creq = new Request(); - Response cres = new Response(); - HttpResponse nRes = getResponse(cres); - - BodyWriter out = new BodyWriter(4096); - out.setConnector(this, res); - - res.setHttpResponse(nRes, out); - - cres.setRequest(creq); - cres.setHook(new ActionHook() { - public void action(ActionCode actionCode, - Object param) { - } - }); - - BodyReader in = new BodyReader(); - in.setConnector(this, req); - HttpRequest nReq = getRequest(creq); - req.setHttpRequest(nReq, in); - - } - - - // ---- Coyote Adapter interface --- - - @Override - public void service(Request creq, Response cres) throws Exception { - long t0 = System.currentTimeMillis(); - - // compute decodedURI - not done by connector - UriNormalizer.decodeRequest(creq.decodedURI(), creq.requestURI(), creq.getURLDecoder()); - - // find the facades - ServletRequestImpl req = (ServletRequestImpl) creq.getNote(ADAPTER_REQ_NOTE); - ServletResponseImpl res = (ServletResponseImpl) cres.getNote(ADAPTER_RES_NOTE); - - - if (req == null) { - req = new ServletRequestImpl(); - res = req.getResponse(); - - BodyReader in = new BodyReader(); - in.setConnector(this, req); - - HttpRequest nReq = getRequest(creq); - nReq.setServerPort(creq.getServerPort()); - HttpResponse nRes = getResponse(cres); - - req.setHttpRequest(nReq, in); - BodyWriter out = new BodyWriter(4096); - out.setConnector(this, res); - - res.setHttpResponse(nRes, out); - - creq.setNote(ADAPTER_REQ_NOTE, req); - cres.setNote(ADAPTER_RES_NOTE, res); - - } - req.setConnector(this); - - try { - lite.service(req, res); - } catch(IOException ex) { - throw ex; - } catch( Throwable t ) { - t.printStackTrace(); - } finally { - long t1 = System.currentTimeMillis(); - -// log.info("<<<<<<<< DONE: " + creq.method() + " " + -// creq.decodedURI() + " " + -// res.getStatus() + " " + -// (t1 - t0)); - - // Final processing - // TODO: only if not commet, this doesn't work with the - // other connectors since we don't have the info - // TODO: add this note in the nio/apr connectors - // TODO: play nice with TomcatLite, other adapters that flush/close - if (cres.getNote(COMET_RES_NOTE) == null) { - - if (!res.isCommitted()) { - cres.sendHeaders(); - } - res.getOutputBuffer().flush(); - - BodyWriter mw = res.getBodyWriter(); - //MessageWriter.getWriter(creq, cres, 0); - mw.flush(); - mw.recycle(); - - BodyReader reader = req.getBodyReader(); - //getReader(creq); - reader.recycle(); - - cres.finish(); - - creq.recycle(); - cres.recycle(); - - req.recycle(); - res.recycle(); - } - } - } - - @Override - public boolean event(Request req, Response res, SocketStatus status) - throws Exception { - return false; - } - - - public void setTomcatLite(TomcatLite lite) { - this.lite = lite; - } - - - public String getRemoteHost(HttpServletRequest hreq) { - ServletRequestImpl req = (ServletRequestImpl) hreq; - - Request creq = (Request) req.getHttpRequest().nativeRequest; - creq.action(ActionCode.ACTION_REQ_HOST_ATTRIBUTE, creq); - return creq.remoteHost().toString(); - } - - public String getRemoteAddr(HttpServletRequest hreq) { - ServletRequestImpl req = (ServletRequestImpl) hreq; - - Request creq = (Request) req.getHttpRequest().nativeRequest; - creq.action(ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, creq); - return creq.remoteAddr().toString(); - } - - - @Override - public void beforeClose(HttpServletResponse res, int len) throws IOException { - Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; - - if ((!cres.isCommitted()) - && (cres.getContentLengthLong() == -1)) { - // Flushing the char buffer - // If this didn't cause a commit of the response, the final content - // length can be calculated - if (!cres.isCommitted()) { - cres.setContentLength(len); - } - } - } - - public int doRead(ServletRequestImpl hreq, ByteChunk bb) throws IOException { - ServletRequestImpl req = (ServletRequestImpl) hreq; - - Request creq = (Request) req.getHttpRequest().nativeRequest; - return creq.doRead(bb); - } - - @Override - public void doWrite(HttpServletResponse res, ByteChunk chunk) - throws IOException { - Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; - cres.doWrite(chunk); - - } - - - @Override - public void realFlush(HttpServletResponse res) throws IOException { - Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; - cres.action(ActionCode.ACTION_CLIENT_FLUSH, - cres); - // If some exception occurred earlier, or if some IOE occurred - // here, notify the servlet with an IOE - if (cres.isExceptionPresent()) { - throw new ClientAbortException - (cres.getErrorException()); - } - - } - - - @Override - public void sendHeaders(HttpServletResponse res) throws IOException { - Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; - - // This should happen before 'prepareResponse' is called !! - // Now update coyote response based on response - // don't set charset/locale - they're computed in lite - cres.setContentType(res.getContentType()); - cres.sendHeaders(); - } - - @Override - public void finishResponse(HttpServletResponse res) throws IOException { - Response cres = (Response) ((ServletResponseImpl) res).getHttpResponse().nativeResponse; - cres.finish(); - } - - protected int port = 8800; - protected boolean daemon = false; - - /** - * Note indicating the response is COMET. - */ - public static final int COMET_RES_NOTE = 2; - public static final int COMET_REQ_NOTE = 2; - - public static final int ADAPTER_RES_NOTE = 1; - public static final int ADAPTER_REQ_NOTE = 1; - - protected ProtocolHandler proto; - - //protected Adapter adapter = new MapperAdapter(); - protected int maxThreads = 20; - boolean started = false; - boolean async = false; // use old nio connector - - protected ObjectManager om; - - - public void setObjectManager(ObjectManager om) { - this.om = om; - } - - /** - * Add an adapter. If more than the 'default' adapter is - * added, a MapperAdapter will be inserted. - * - * @param path Use "/" for the default. - * @param adapter - */ -// public void addAdapter(String path, Adapter added) { -// if ("/".equals(path)) { -// ((MapperAdapter) adapter).setDefaultAdapter(added); -// } else { -// ((MapperAdapter) adapter).getMapper().addWrapper(path, added); -// } -// } - - /** - */ - public void run() { - try { - init(); - start(); - } catch(IOException ex) { - ex.printStackTrace(); - } - } - - public void setDaemon(boolean b) { - daemon = b; - } - - protected void initAdapters() { - if (proto == null) { - addProtocolHandler(port, daemon); - } - // adapter = ... - // Adapter secondaryadapter = ... - //registry.registerComponent(secondaryadapter, ":name=adapter", null); - } - - public void stop() throws Exception { - if (!started) { - return; - } - proto.destroy(); - started = false; - } - -// /** -// * Simple CLI support - arg is a path:className pair. -// */ -// public void setAdapter(String arg) { -// String[] pathClass = arg.split(":", 2); -// try { -// Class c = Class.forName(pathClass[1]); -// Adapter a = (Adapter) c.newInstance(); -// addAdapter(pathClass[0],a); -// } catch (Throwable e) { -// e.printStackTrace(); -// } -// } - - public void setConnector(ProtocolHandler h) { - this.proto = h; - h.setAttribute("port", Integer.toString(port)); - - om.bind("ProtocolHandler:" + "ep-" + port, proto); - } - - public void addProtocolHandler(int port, boolean daemon) { - Http11NioProtocol proto = new Http11NioProtocol(); - proto.setCompression("on"); - proto.setCompressionMinSize(32); - proto.setPort(port); - proto.getEndpoint().setDaemon(daemon); - setConnector(proto); - setPort(port); - setDaemon(daemon); - } - - public void addProtocolHandler(ProtocolHandler proto, - int port, boolean daemon) { - setConnector(proto); - setPort(port); - setDaemon(daemon); - } - - public void setPort(int port) { - if (proto != null) { - proto.setAttribute("port", Integer.toString(port)); - } - this.port = port; - } - - - public void init() { - //JdkLoggerConfig.loadCustom(); - om.bind("CoyoteConnector:" + "CoyoteConnector-" + port, - this); - } - - - public void start() throws IOException { - try { - if (started) { - return; - } - init(); - initAdapters(); - - // not required - should run fine without a connector. - if (proto != null) { - proto.setAdapter(this); - - proto.init(); - proto.start(); - } - - started = true; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - public boolean getStarted() { - return started; - } - - public boolean asyncDispatch(Request req,Response res, SocketStatus status) throws Exception { - // implement me - return false; - } - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/IOStatus.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/IOStatus.java new file mode 100644 index 000000000..fee4d188b --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/IOStatus.java @@ -0,0 +1,59 @@ +/* + */ +package org.apache.tomcat.lite.service; + +import java.io.IOException; +import java.util.Map; + +import org.apache.tomcat.lite.http.HttpRequest; +import org.apache.tomcat.lite.http.HttpResponse; +import org.apache.tomcat.lite.http.HttpWriter; +import org.apache.tomcat.lite.http.HttpChannel.HttpService; +import org.apache.tomcat.lite.http.HttpConnector.ConnectionPool; +import org.apache.tomcat.lite.http.HttpConnector.RemoteServer; +import org.apache.tomcat.lite.io.IOChannel; + +/** + * Dump status of a connection pool. + */ +public class IOStatus implements HttpService { + + private ConnectionPool pool; + + public IOStatus() { + + } + + public IOStatus(ConnectionPool pool) { + this.pool = pool; + } + + @Override + public void service(HttpRequest httpReq, HttpResponse httpRes) + throws IOException { + ConnectionPool sc = pool == null ? + httpReq.getHttpChannel().getConnector().cpool : + pool; + HttpWriter out = httpRes.getBodyWriter(); + + httpRes.setContentType("text/plain"); + out.println("hosts=" + sc.getTargetCount()); + out.println("waiting=" + sc.getSocketCount()); + out.println("closed=" + sc.getClosedSockets()); + out.println(); + + for (Map.Entry e: sc.hosts.entrySet()) { + out.append(e.getKey()); + out.append("="); + out.println(Integer.toString(e.getValue().connections.size())); + + for (IOChannel ch: e.getValue().connections) { + out.println(ch.getId() + + " " + ch.toString()); + } + out.println(); + } + + } + +} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/JMXProxy.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/JMXProxy.java new file mode 100644 index 000000000..284ee2577 --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/JMXProxy.java @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.tomcat.lite.service; + + +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import org.apache.tomcat.integration.DynamicObject; +import org.apache.tomcat.integration.ObjectManager; + +/** + * Send all registered JMX objects and properties as JSON. + * + * Based on JMXProxy servlet, but: + * - Async handler instead of servlet - so it works with 'raw' connector + * - doesn't use JMX - integrates with the ObjectManager ( assuming OM + * provies a list of managed objects ) + * - all the reflection magic from modeler is implemented here. + * + * @author Costin Manolache + */ +public class JMXProxy extends ObjectManager implements Runnable { + + static Logger log = Logger.getLogger(JMXProxy.class.getName()); + + protected ObjectManager om; + + Map types = new HashMap(); + + Map objects = new HashMap(); + + + public void bind(String name, Object o) { + objects.put(name, o); + } + + public void unbind(String name) { + objects.remove(name); + } + + + public void setObjectManager(ObjectManager om) { + this.om = om; + } + + + private DynamicObject getClassInfo(Class beanClass) { + if (types.get(beanClass) != null) { + return types.get(beanClass); + } + DynamicObject res = new DynamicObject(beanClass); + types.put(beanClass, res); + return res; + } + + + // --------------------------------------------------------- Public Methods + + public void getAttribute(PrintWriter writer, String onameStr, String att) { + try { + + Object bean = objects.get(onameStr); + Class beanClass = bean.getClass(); + DynamicObject ci = getClassInfo(beanClass); + + Object value = ci.getAttribute(bean, att); + writer.println("OK - Attribute get '" + onameStr + "' - " + att + + "= " + escape(value.toString())); + } catch (Exception ex) { + writer.println("Error - " + ex.toString()); + } + } + + + public void setAttribute( PrintWriter writer, + String onameStr, String att, String val ) + { + try { + Object bean = objects.get(onameStr); + Class beanClass = bean.getClass(); + DynamicObject ci = getClassInfo(beanClass); + + ci.setProperty(bean, att, val); + writer.println("OK - Attribute set"); + } catch( Exception ex ) { + writer.println("Error - " + ex.toString()); + } + } + + public void listBeans( PrintWriter writer, String qry, boolean json ) + { + if (json) { + listBeansJson(writer, qry); + return; + } + Set names = objects.keySet(); + writer.println("OK - Number of results: " + names.size()); + writer.println(); + + Iterator it=names.iterator(); + while( it.hasNext()) { + String oname=it.next(); + writer.println( "Name: " + oname); + + try { + Object bean = objects.get(oname); + Class beanClass = bean.getClass(); + DynamicObject ci = getClassInfo(beanClass); + writer.println("modelerType: " + beanClass.getName()); + + Object value=null; + for (String attName: ci.attributeNames()) { + try { + value = ci.getAttribute(bean, attName); + } catch( Throwable t) { + System.err.println("Error getting attribute " + oname + + " " + attName + " " + t.toString()); + continue; + } + if( value==null ) continue; + String valueString=value.toString(); + writer.println( attName + ": " + escape(valueString)); + } + } catch (Exception e) { + // Ignore + } + writer.println(); + } + + } + + private static void json(PrintWriter writer, String name, String value) { + writer.write("\"" + name +"\":" + "\"" + escapeJson(value) + "\","); + } + + private void listBeansJson(PrintWriter writer, String qry) { + Set names = objects.keySet(); + writer.println("["); + + Iterator it=names.iterator(); + while( it.hasNext()) { + writer.print("{"); + String oname=it.next(); + json(writer, "name", oname); + + try { + Object bean = objects.get(oname); + Class beanClass = bean.getClass(); + DynamicObject ci = getClassInfo(beanClass); + json(writer, "modelerType", beanClass.getName()); + + Object value=null; + for (String attName: ci.attributeNames()) { + try { + value = ci.getAttribute(bean, attName); + } catch( Throwable t) { + System.err.println("Error getting attribute " + oname + + " " + attName + " " + t.toString()); + continue; + } + if( value==null ) continue; + String valueString=value.toString(); + json(writer, attName, valueString); + } + writer.println("}"); + } catch (Exception e) { + // Ignore + } + } + writer.println("]"); + } + + public static String escapeJson(String value) { + return value; + } + + public static String escape(String value) { + // The only invalid char is \n + // We also need to keep the string short and split it with \nSPACE + // XXX TODO + int idx=value.indexOf( "\n" ); + if( idx < 0 ) return value; + + int prev=0; + StringBuffer sb=new StringBuffer(); + while( idx >= 0 ) { + appendHead(sb, value, prev, idx); + + sb.append( "\\n\n "); + prev=idx+1; + if( idx==value.length() -1 ) break; + idx=value.indexOf('\n', idx+1); + } + if( prev < value.length() ) + appendHead( sb, value, prev, value.length()); + return sb.toString(); + } + + private static void appendHead( StringBuffer sb, String value, int start, int end) { + if (end < 1) return; + + int pos=start; + while( end-pos > 78 ) { + sb.append( value.substring(pos, pos+78)); + sb.append( "\n "); + pos=pos+78; + } + sb.append( value.substring(pos,end)); + } + + public boolean isSupported( String type ) { + return true; + } + + @Override + public void run() { + + } +} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/LogConfig.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/LogConfig.java new file mode 100644 index 000000000..bbb52246a --- /dev/null +++ b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/LogConfig.java @@ -0,0 +1,64 @@ +/* + * Copyright 2001-2004 The Apache Software Foundation. + * Copyright 2004 Costin Manolache + * + * 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.tomcat.lite.service; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.tomcat.lite.http.HttpRequest; +import org.apache.tomcat.lite.http.HttpResponse; +import org.apache.tomcat.lite.http.HttpChannel.HttpService; + +/** + * Log configuration + * + */ +public class LogConfig implements HttpService { + + /** + * Framework can set this attribute with comma separated + * list of loggers to set to debug level. + * This is used at startup. + */ + public void setDebug(String debug) { + for (String log : debug.split(",")) { + Logger logger = Logger.getLogger(log); + logger.setLevel(Level.INFO); + } + } + + /** + * + */ + public void setWarn(String nodebug) { + for (String log : nodebug.split(",")) { + Logger logger = Logger.getLogger(log); + logger.setLevel(Level.WARNING); + } + } + + @Override + public void service(HttpRequest httpReq, HttpResponse httpRes) + throws IOException { + String debug = httpReq.getParameter("debug"); + setDebug(debug); + String warn = httpReq.getParameter("warn"); + setWarn(warn); + } +} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/ServletContextConfig.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/ServletContextConfig.java deleted file mode 100644 index 59dbd62e9..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/ServletContextConfig.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite.webxml; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; - -/** - * All the data in web.xml, annotations, etc should be represented - * here. This class is serializable. - * - * Public fields to make it easy to access it. - * Naming should match the web.xml element name. - * - * @author Costin Manolache - */ -public class ServletContextConfig implements Serializable { - - private static final long serialVersionUID = 1728492145981883124L; - - public String fileName; - public long timestamp; - - public boolean full; - - public String displayName; - - public HashMap contextParam = new HashMap(); - - public HashMap mimeMapping = new HashMap(); // extension -> mime-type - - public ArrayList listenerClass = new ArrayList(); - - public ArrayList welcomeFileList = new ArrayList(); - - // code -> location - public HashMap errorPageCode= new HashMap(); - - // exception -> location - public HashMap errorPageException= new HashMap(); - - public HashMap localeEncodingMapping= new HashMap(); // locale -> encoding - - // public HashMap tagLibs; // uri->location - // jsp-property-group - - // securityConstraint - public ArrayList securityConstraint = new ArrayList(); - - // loginConfig - public String authMethod; - public String realmName; - public String formLoginPage; - public String formErrorPage; - - public ArrayList securityRole = new ArrayList(); - - // envEntry - public ArrayList envEntry = new ArrayList(); - - // ejbRef - // ejbLocalRef - // serviceRef - // resourceRef - // resourceEnvRef - // message-destination - // message-destinationRef - public HashMap filters = new HashMap(); - public HashMap servlets = new HashMap(); - - public int sessionTimeout; - public boolean distributable; - - public HashMap servletMapping = new HashMap(); // url -> servlet - public ArrayList filterMappings = new ArrayList(); - - - public static class FilterData implements Serializable { - private static final long serialVersionUID = -535820271746973166L; - - public HashMap initParams = new HashMap(); - public String filterClass; - public String filterName; - } - - // Normalized - public static class FilterMappingData implements Serializable { - private static final long serialVersionUID = -4533568066713041994L; - public String filterName; - - // Only one of the 2 - public String urlPattern; - public String servletName; - - // REQUEST, FORWARD, INCLUDE, ERROR, ASYNC - public ArrayList dispatcher = new ArrayList(); - } - - public static class EnvEntryData implements Serializable { - private static final long serialVersionUID = 7023847615343715257L; - public String envEntryName; - public String envEntryType; - public String envEntryValue; - } - - public static class ServletData implements Serializable { - private static final long serialVersionUID = -3216904178501185930L; - - public ServletData() { - } - public ServletData(String servletName, String servletClass) { - this.servletClass = servletClass; - this.servletName = servletName; - } - - public HashMap initParams = new HashMap(); - public String servletName; - public String servletClass; - public String jspFile; - public int loadOnStartup = -1; - public String runAs; - public HashMap securityRoleRef = new HashMap(); // roleName -> [roleLink] - - } - - public static class WebResourceCollectionData implements Serializable { - public String webResourceName; - public ArrayList urlPattern = new ArrayList(); - public ArrayList httpMethod = new ArrayList(); - } - - public static class SecurityConstraintData implements Serializable { - private static final long serialVersionUID = -4780214921810871769L; - - public ArrayList roleName = new ArrayList(); // auth-constraint/role - - public ArrayList webResourceCollection = - new ArrayList(); - public String transportGuarantee; - - } -} \ No newline at end of file diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/TomcatLiteWebXmlConfig.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/TomcatLiteWebXmlConfig.java deleted file mode 100644 index c79fa14ce..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/TomcatLiteWebXmlConfig.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite.webxml; - -import javax.servlet.ServletContext; -import javax.servlet.ServletException; - -import org.apache.tomcat.lite.ContextPreinitListener; -import org.apache.tomcat.lite.ServletContextImpl; - -/** - * Default configurator - parse web.xml, init the context. - * - * Will be executed first - if set as the default config addon. - * - * Possible extensions: - * - read from a .ser file instead of web.xml - * - custom code for manual/extra config - * - read from a central repo - * - * @author Costin Manolache - */ -public class TomcatLiteWebXmlConfig implements ContextPreinitListener { - - protected void readWebXml(ServletContextImpl ctx, - String base) throws ServletException { - // TODO: .ser, reloading, etc -// if (contextConfig != null && contextConfig.fileName != null) { -// // TODO: this should move to deploy - if not set, there is no point -// File f = new File(contextConfig.fileName); -// if (f.exists()) { -// if (f.lastModified() > contextConfig.timestamp + 1000) { -// log("Reloading web.xml"); -// contextConfig = null; -// } -// } else { -// log("Old web.xml"); -// contextConfig = null; -// } -// } - if (base != null) { - WebXml webXml = new WebXml(ctx.getContextConfig()); - webXml.readWebXml(base); - } - } - - @Override - public void preInit(ServletContext ctx) { - ServletContextImpl servletContext = - (ServletContextImpl) ctx; - - String base = servletContext.getBasePath(); - if (base == null) { - return; // nothing we can do - } - try { - readWebXml(servletContext, base); - } catch (ServletException e) { - // TODO Auto-generated catch block - throw new RuntimeException(e); - } - } - -} diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/WebXml.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/WebXml.java deleted file mode 100644 index 6f4c08a12..000000000 --- a/modules/tomcat-lite/java/org/apache/tomcat/lite/webxml/WebXml.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.tomcat.lite.webxml; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; - -import javax.servlet.ServletException; - -import org.apache.tomcat.lite.webxml.ServletContextConfig.EnvEntryData; -import org.apache.tomcat.lite.webxml.ServletContextConfig.FilterData; -import org.apache.tomcat.lite.webxml.ServletContextConfig.FilterMappingData; -import org.apache.tomcat.lite.webxml.ServletContextConfig.SecurityConstraintData; -import org.apache.tomcat.lite.webxml.ServletContextConfig.ServletData; -import org.apache.tomcat.lite.webxml.ServletContextConfig.WebResourceCollectionData; -import org.apache.tomcat.util.DomUtil; -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -/** - * General-purpose utility to process an web.xml file. Result - * is a tree of objects starting with WebAppData. - * - * TODO: allow writting of web.xml, allow modification ( preserving - * comments ) - * - * @author costin - */ -public class WebXml { - ServletContextConfig d; - - public WebXml(ServletContextConfig cfg) { - d = cfg; - } - - public ServletContextConfig getWebAppData() { - return d; - } - - public void readWebXml(String baseDir) throws ServletException { - try { - File webXmlFile = new File( baseDir + "/WEB-INF/web.xml"); - if (!webXmlFile.exists()) { - return; - } - d.fileName = webXmlFile.getCanonicalPath(); - d.timestamp = webXmlFile.lastModified(); - - FileInputStream fileInputStream = new FileInputStream(webXmlFile); - readWebXml(fileInputStream); - } catch (Exception e) { - e.printStackTrace(); - throw new ServletException(e); - } - } - - public void readWebXml(InputStream fileInputStream) - throws ServletException { - try { - - Document document = DomUtil.readXml(fileInputStream); - Node webappNode = DomUtil.getChild(document, "web-app"); - - String fullS = DomUtil.getAttribute(webappNode, "full"); - if (fullS != null && fullS.equalsIgnoreCase("true")) { - d.full = true; - } - - d.displayName = DomUtil.getAttribute(webappNode, "display-name"); - - // Process each child of web-app - Node confNode = DomUtil.getChild(webappNode, "filter"); - while (confNode != null ) { - processFilter(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "filter-mapping"); - while (confNode != null ) { - processFilterMapping(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "context-param"); - while (confNode != null ) { - String n = DomUtil.getChildContent(confNode, "param-name").trim(); - String v = DomUtil.getChildContent(confNode, "param-value").trim(); - d.contextParam.put(n, v); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "mime-mapping"); - while (confNode != null ) { - String n = DomUtil.getChildContent(confNode, "extension"); - String t = DomUtil.getChildContent(confNode, "mime-type"); - d.mimeMapping.put(n, t); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "error-page"); - while (confNode != null ) { - processErrorPage(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "jsp-config"); - while (confNode != null ) { - processJspConfig(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "servlet"); - while (confNode != null ) { - processServlet(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "servlet-mapping"); - while (confNode != null ) { - processServletMapping(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "listener"); - while (confNode != null ) { - String lClass = DomUtil.getChildContent(confNode, "listener-class"); - d.listenerClass.add(lClass); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "security-constraint"); - while (confNode != null ) { - processSecurityConstraint(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "login-config"); - while (confNode != null ) { - processLoginConfig(confNode); - confNode = DomUtil.getNext(confNode); - if (confNode != null) - throw new ServletException("Multiple login-config"); - } - - confNode = DomUtil.getChild(webappNode, "session-config"); - while (confNode != null ) { - String n = DomUtil.getChildContent(confNode, "session-timeout"); - int stout = Integer.parseInt(n); - d.sessionTimeout = stout; - confNode = DomUtil.getNext(confNode); - if (confNode != null) - throw new ServletException("Multiple session-config"); - } - - confNode = DomUtil.getChild(webappNode, "welcome-file-list"); - while (confNode != null ) { - Node wf = DomUtil.getChild(confNode, "welcome-file"); - while (wf != null) { - String file = DomUtil.getContent(wf); - d.welcomeFileList.add(file); - wf = DomUtil.getNext(wf); - } - // more sections ? - confNode = DomUtil.getNext(confNode); - } - - // Not supported right now - TODO: collect, have jndi plugin - confNode = DomUtil.getChild(webappNode, "env-entry"); - while (confNode != null ) { - processEnvEntry(confNode); - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(webappNode, "locale-encoding-mapping-list"); - while (confNode != null ) { - confNode = DomUtil.getNext(confNode); - String n = DomUtil.getChildContent(confNode, "locale"); - String t = DomUtil.getChildContent(confNode, "encoding"); - d.localeEncodingMapping.put(n, t); - } - - confNode = DomUtil.getChild(webappNode, "distributable"); - while (confNode != null ) { - d.distributable = true; - confNode = DomUtil.getNext(confNode); - } - - confNode = DomUtil.getChild(confNode, "security-role"); - while (confNode != null ) { - String n = DomUtil.getChildContent(confNode, "role-name"); - d.securityRole.add(n); - confNode = DomUtil.getNext(confNode); - } - - } catch (Exception e) { - e.printStackTrace(); - throw new ServletException(e); - } - } - - private void processJspConfig(Node confNode) { - Node tagLib = DomUtil.getChild(confNode, "taglib"); - while (tagLib != null) { - String uri = DomUtil.getChildContent(tagLib, "taglib-uri"); - String l = DomUtil.getChildContent(tagLib, "taglib-location"); - //d.tagLibs.put(uri, l); - tagLib = DomUtil.getNext(tagLib); - } - - tagLib = DomUtil.getChild(confNode, "jsp-property-group"); - while (tagLib != null) { - // That would be the job of the JSP servlet to process. - tagLib = DomUtil.getNext(tagLib); - } - } - - private void processEnvEntry(Node confNode) { - EnvEntryData ed = new EnvEntryData(); - ed.envEntryName = DomUtil.getChildContent(confNode,"env-entry-name"); - ed.envEntryType = DomUtil.getChildContent(confNode,"env-entry-type"); - ed.envEntryValue = DomUtil.getChildContent(confNode,"env-entry-value"); - d.envEntry.add(ed); - } - - private void processLoginConfig(Node confNode) { - d.authMethod = DomUtil.getChildContent(confNode,"auth-method"); - d.realmName = DomUtil.getChildContent(confNode,"auth-method"); - Node formNode = DomUtil.getChild(confNode, "form-login-config"); - if (formNode != null) { - d.formLoginPage = DomUtil.getChildContent(formNode,"form-login-page"); - d.formErrorPage = DomUtil.getChildContent(formNode,"form-error-page"); - } - } - - private void processSecurityConstraint(Node confNode) { - SecurityConstraintData sd = new SecurityConstraintData(); - Node cn = DomUtil.getChild(confNode, "web-resource-collection"); - while (cn != null) { - WebResourceCollectionData wrd = new WebResourceCollectionData(); - wrd.webResourceName = DomUtil.getChildContent(cn, "web-resource-name"); - Node scn = DomUtil.getChild(cn,"url-pattern"); - while (scn != null) { - wrd.urlPattern.add(DomUtil.getContent(scn)); - scn = DomUtil.getNext(scn); - } - scn = DomUtil.getChild(cn,"http-method"); - while (scn != null) { - wrd.httpMethod.add(DomUtil.getContent(scn)); - scn = DomUtil.getNext(scn); - } - cn = DomUtil.getNext(cn); - sd.webResourceCollection.add(wrd); - } - - d.securityConstraint.add(sd); - } - - private void processErrorPage(Node confNode) { - String name = DomUtil.getChildContent(confNode,"location"); - String c = DomUtil.getChildContent(confNode,"error-code"); - String t = DomUtil.getChildContent(confNode,"exception-type"); - if (c != null) { - d.errorPageCode.put(c, name); - } - if (t != null) { - d.errorPageException.put(t, name); - } - } - - private void processServlet(Node confNode) throws ServletException { - ServletData sd = new ServletData(); - - sd.servletName = DomUtil.getChildContent(confNode,"servlet-name"); - sd.servletClass = DomUtil.getChildContent(confNode,"servlet-class"); - sd.jspFile = DomUtil.getChildContent(confNode,"jsp-file"); - - processInitParams(confNode, sd.initParams); - - d.servlets.put( sd.servletName, sd ); - - String los = DomUtil.getChildContent(confNode, "load-on-startup"); - if (los != null ) { - sd.loadOnStartup = Integer.parseInt(los); - } - - Node sn = DomUtil.getChild(confNode, "security-role-ref"); - while (sn != null ) { - String roleName = DomUtil.getChildContent(sn, "role-name"); - String roleLink = DomUtil.getChildContent(sn, "role-link"); - if (roleLink == null) { - sd.securityRoleRef.put(roleName, ""); - } else { - sd.securityRoleRef.put(roleName, roleLink); - } - sn = DomUtil.getNext(sn); - } - } - - private void processInitParams(Node confNode, HashMap initParams) { - Node initN = DomUtil.getChild(confNode, "init-param"); - while (initN != null ) { - String n = DomUtil.getChildContent(initN, "param-name"); - String v = DomUtil.getChildContent(initN, "param-value"); - initParams.put(n, v); - initN = DomUtil.getNext(initN); - } - } - - private void processServletMapping(Node confNode) { - String name = DomUtil.getChildContent(confNode,"servlet-name"); - Node dataN = DomUtil.getChild(confNode, "url-pattern"); - while (dataN != null) { - String path = DomUtil.getContent(dataN).trim(); - dataN = DomUtil.getNext(dataN); - - if (! (path.startsWith("/") || path.startsWith("*"))) { - // backward compat - path = "/" + path; - } - d.servletMapping.put(path, name); - } - } - - private void processFilterMapping(Node confNode) { - String filterName = DomUtil.getChildContent(confNode,"filter-name"); - // multiple - ArrayList dispatchers = new ArrayList(); - Node dataN = DomUtil.getChild(confNode, "dispatcher"); - while (dataN != null ) { - String d = DomUtil.getContent(dataN); - dispatchers.add(d); - dataN = DomUtil.getNext(dataN); - } - - // Multiple url-pattern and servlet-name in one - // mapping rule. Need to be applied in order. - dataN = DomUtil.getChild(confNode, "url-pattern"); - while (dataN != null ) { - FilterMappingData fm = new FilterMappingData(); - fm.filterName = filterName; - fm.dispatcher = dispatchers; - String path = DomUtil.getContent(dataN); - dataN = DomUtil.getNext(dataN); - fm.urlPattern = path; - d.filterMappings.add(fm); - } - dataN = DomUtil.getChild(confNode, "servlet-name"); - while (dataN != null ) { - FilterMappingData fm = new FilterMappingData(); - fm.filterName = filterName; - fm.dispatcher = dispatchers; - String sn = DomUtil.getContent(dataN); - dataN = DomUtil.getNext(dataN); - fm.servletName = sn; - d.filterMappings.add(fm); - } - } - - private void processFilter(Node confNode) { - String name = DomUtil.getChildContent(confNode,"filter-name"); - String sclass = DomUtil.getChildContent(confNode,"filter-class"); - - FilterData fd = new FilterData(); - processInitParams(confNode, fd.initParams); - fd.filterName = name; - fd.filterClass = sclass; - d.filters.put(name, fd); - } - -}