You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Jun Inamori <j-...@osa.att.ne.jp> on 2000/04/05 01:18:20 UTC
Hello!
Hello,
Thank you for your enthusiastic effort.
I've tried Tomcat3.1Beta and it runs well itself.
But I found some problem when called from JServ module.
The JServ module was complied from the source which is supplied with the
source distribution of Tomcat3.1Beta.
And after struggled with it, I found the workaround for it.
So I'd like to subscribe this mailing list and contribute some code.
For details about JServ, see:
http://java.apache.org/
The configuration example about the JServ module and the Tomcat server
is described in the section 4 of this report.
After solved the problems, I have some suggestions to you.
So this report consists of 5 sections:
1:Problems
2:Solutions
3:Suggestions
4:Configuration example
5:Source code
1:Problems
P1.
When JServe module is configured as 'Autostart'(which means that Tomcat
is started by Apache http server), the module watches the state of
Tomcat server by calling 'ping' through the socket(normally AJP12
connection). 'AJP12RequestAdapter'(which acts as the gateway from the
JServ module to Tomcat and vice versa) can response to it, but throws
exception after the response.
P2.
We can configure the JServ module to map the mount point to the context.
For example, the line
ApjServMount /myexample /examples
in the configuration file suggests the module to translate the HTTP
request such as 'http://myhost.com/myexample/snoop' to the AJP12 request
'ajpv12://localhost/example/snoop'. This means that Tomcat server must
process the request to the servlet 'snoop' in the context 'examples',
but it tries to fined the context 'myexample' and fail.
P3.
JServ module can report the configuration both of JServ module itself
and the corresponding Servlet server. The status of the Servlet server
must be retrieved from the servlet which is responsible for gathering
the configuration of the servlet server. But Tomcat server does not
implement such a servlet.
2:Solutions
S1. The solution for P1 above:
After the 'protected void readNextRequest()' method of
'org.apache.tomcat.service.connector.AJP12RequestAdapter'(which is
called within 'public void processConnectioin()' of
'org.apache.tomcat.service.connector.Ajp12ConnectionHandler') responds
to the 'ping' request from JServ module, it closes the socket. At this
point, no farther processing have to be done. But 'processConnectioin()'
of 'Ajp12ConnectionHandler' tries to process the request and this
results in 'IOException'. To prevent 'processConnectioin()' to continue
processing, it must know the request was just the one for the 'ping'.
The following are the quotes from my modified source:
public void processConnection(TcpConnection connection, Object[]
theData) {
--- ignored ---
reqA.readNextRequest();
if( reqA.shutdown )
return;
//Jun Inamori modified
if( reqA.isPing )
return;
--- ignored ---
}
class AJP12RequestAdapter extends RequestImpl {
--- ignored ---
boolean shutdown=false;
// Jun Inamori modified
boolean isPing=false;
--- ignored ---
protected void readNextRequest() throws IOException {
--- ignored ---
case 254: // Signal
signal = ajpin.read();
if (signal == 0) { // PING implemented as signal
//Jun Inamori modified
isPing=true;
--- ignored ---
}
S2. The solution for P2 above:
While the JServ module sends the appropriate request to Tomcat server,
'Ajp12ConnectionHandler' tries to parse the original request uri to find
the context and the servlet wrapper. For example, when the original
request to the Apache http server is
'http://myhost.com/myexample/myservlet' and the path '/myexample' is
mapped to the context '/example' on the JServ module, the JServ module
dispatches the following request to the Tomcat server:
ContextName: example
ServletName: myservlet
RequestURI: /myexample/myservlet
Note that the context name is removed of the first slash character, i.e.
it is 'example' and not '/example'.
This means that 'readNextRequest' method of 'Ajp12ConnectionHandler'
fails to fined the actual context and 'service' method of
'ContextManager' tells all the interceptors to parse the request uri
(/myexample/myservlet) to get the actual contexts and the servlet
wrappers. But, of course, the context '/myexample' does not exist in the
ContextManager.
The solution is to provide 'AJP12RequestAdapter' with the request uri
suitable for retrieving both of the context name and the servlet name.
Yes, while the request uri must be '/myexample/myservlet' on the JServ
module side, it must be '/example/myservle' on the Tomcat server side.
The following are the quotes from my modified source:
protected void readNextRequest() throws IOException {
--- ignored ---
marker = ajpin.read();
switch (marker) {
--- ignored ---
case 1: //beginning of request
--- ignored ---
requestURI = ajpin.readString(""); //request uri
//Jun Inamori modified;
// We need to fix the request URI here.
if(contextPath!=null && contextPath.length()>0){
// The context of 'engine' means that
// the request is dispatched from
// 'jserv-status' function.
if(contextPath.equals("engine") && servletName!=null){
if(queryString!=null){
requestURI="/"+contextPath+"/"+servletName+"?"+queryString;
}
else{
requestURI="/"+contextPath+"/"+servletName;
}
}
else if(!requestURI.startsWith("/"+contextPath)){
int index=requestURI.indexOf("/",1);
if(index>0){
String original_lookup=requestURI.substring(index);
if(queryString!=null){
requestURI="/"+contextPath+original_lookup+"?"+queryString;
}
else{
requestURI="/"+contextPath+original_lookup;
if(pathInfo==null && !requestURI.endsWith("/")){
if(servletName==null){
requestURI=requestURI+"/";
pathInfo="/";
}
else if(!requestURI.endsWith(servletName)){
requestURI=requestURI+"/";
pathInfo="/";
}
}
}
}
else{
if(queryString!=null){
requestURI="/"+contextPath+"?"+queryString;
}
else{
requestURI="/"+contextPath;
if(pathInfo==null && !requestURI.endsWith("/")){
requestURI=requestURI+"/";
pathInfo="/";
}
}
}
}
}
--- ignored ---
}
S3. The solution for P3 above:
I'm not sure whether 'org.apache.tomcat.startup.Tomcat' itself should be
implemented as such a servlet. So I developed another class,
'org.apache.tomcat.startup.TomcatJServ', which acts both as the starter
invoked by the JServ module and as the servlet responsible for reporting
the configuration of the Tomcat server. This class wraps
'org.apache.tomcat.startup.Tomcat' and the 'main' method calls
'execute()' method on it. The method of 'doGet' builds up the
ContextManager based on '$TOMCAT_HOME/conf/server.xml', retrieves the
information from it, and then sends it back to the JServ module as HTML
string.
As I describe in the next section, I've not implement this servlet to my
heart content yet. But it works well with the JServ module.
3:Suggestions
I have only one suggestion at this time. It is that all the actual
connector classes should implement the method of 'getEndPoint()', which
returns the 'EndPoint' object. Below, I'll describe the reason why I
need this method.
When designing the servlet above S3, I found some restrictions about the
information retrieved through the ContextManager. As for the contexts
and the servlets held by the contexts, my servlet can retrieve
sufficient information. But this is not true for 'Connector's. The
enumeration of the connectors can be obtained from the ContextManager,
but each connector itself does not provide us with enough information.
Only the port number and the server name are available on the Connector
interface, but there are much more properties, such as the number of
thread pooling, according to the actual implementation class of
connector. Such an important configuration of each connector are kept in
the corresponding 'EndPoint' object, but such an endpoint object is held
as the private variable in each connector.(The connector classes
available at this time do not implement 'getEndPoint()' method.) This
means we have no way to get the 'live' information at that time from
connectors. I've implemented my servlet to read the configuration XML
file again to retrieve the 'static' information for each connector. But
I don't like that way, not only because the default values for the
properties omitted in XML file cannot be obtained, but also because
parsing the same XML file again is performance issue.
4:Configuration example
Now, I've just uploaded the documentation about how to configure the
JServ module and the Tomcat server to my web site.
Please see:
http://www.oop-reserch.com/tomcat/
5:Source code
*** Source of ***
*** org.apache.tomcat.service.connector.Ajp12ConnectionHandler ***
*** I modified the line below the comment of '//Jun Inamori modified'
***
*** Start of source code ***
/*
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software
itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products
derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
/*
Based on Ajp11ConnectionHandler and Ajp12 implementation of JServ
*/
package org.apache.tomcat.service.connector;
import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.tomcat.core.*;
import org.apache.tomcat.util.*;
import org.apache.tomcat.service.http.*;
import org.apache.tomcat.service.http.HttpResponseAdapter;
import org.apache.tomcat.service.http.HttpRequestAdapter;
import org.apache.tomcat.service.*;
import javax.servlet.*;
import javax.servlet.http.*;
/* Deprecated - must be rewriten to the connector model.
*/
public class Ajp12ConnectionHandler implements TcpConnectionHandler {
StringManager sm =
StringManager.getManager("org.apache.tomcat.service");
ContextManager contextM;
public Ajp12ConnectionHandler() {
}
public Object[] init() {
return null;
}
public void setAttribute(String name, Object value ) {
if("context.manager".equals(name) ) {
contextM=(ContextManager)value;
}
}
public void setContextManager( ContextManager contextM ) {
this.contextM=contextM;
}
public void processConnection(TcpConnection connection, Object[]
theData) {
try {
Socket socket=connection.getSocket();
socket.setSoLinger( true, 100);
// socket.setSoTimeout( 1000); // or what ?
// RequestImpl request = new RequestImpl();
AJP12RequestAdapter reqA = new AJP12RequestAdapter(contextM,
socket);
reqA.setContextManager( contextM );
// ResponseImpl response=new ResponseImpl();
AJP12ResponseAdapter resA=new AJP12ResponseAdapter();
InputStream in=socket.getInputStream();
OutputStream out=socket.getOutputStream();
// request.setRequestAdapter(reqA);
// response.setResponseAdapter( resA );
resA.setOutputStream(socket.getOutputStream());
reqA.setResponse(resA);
resA.setRequest(reqA);
reqA.readNextRequest();
if( reqA.shutdown )
return;
//Jun Inamori modified
if( reqA.isPing )
return;
if (resA.getStatus() >= 400) {
resA.finish();
socket.close();
return;
}
// resolve the server that we are for
// XXX is this needed ??
int contentLength =
reqA.getFacade().getIntHeader("content-length");
if (contentLength != -1) {
BufferedServletInputStream sis =
(BufferedServletInputStream)reqA.getInputStream();
sis.setLimit(contentLength);
}
contextM.service( reqA, resA );
resA.finish();
socket.close();
} catch (Exception e) {
// XXX
// this isn't what we want, we want to log the problem somehow
System.out.println("HANDLER THREAD PROBLEM: " + e);
e.printStackTrace();
}
}
}
class AJP12RequestAdapter extends RequestImpl {
StringManager sm =
StringManager.getManager("org.apache.tomcat.service");
Socket socket;
InputStream sin;
Ajpv12InputStream ajpin;
ContextManager contextM;
boolean shutdown=false;
// Jun Inamori modified
boolean isPing=false;
public int doRead() throws IOException {
return ajpin.read();
}
public int doRead( byte b[], int off, int len ) throws IOException
{
return ajpin.read(b,off,len);
}
public AJP12RequestAdapter(ContextManager cm, Socket s) throws
IOException {
this.socket = s;
this.contextM=cm;
sin = s.getInputStream();
in = new BufferedServletInputStream( this );
ajpin = new Ajpv12InputStream(sin);
}
protected void readNextRequest() throws IOException {
String dummy,token1,token2;
int marker;
int signal;
try {
while (true) {
marker = ajpin.read();
switch (marker) {
case 0: //NOP marker useful for testing if stream is OK
break;
case 1: //beginning of request
method = ajpin.readString(null); //Method
contextPath = ajpin.readString(null); //Zone
// GS, the following commented line causes the Apache + Jserv + Tomcat
// combination to hang with a 404!!!
// if("ROOT".equals( contextPath ) ) contextPath="";
if("ROOT".equalsIgnoreCase( contextPath ) ) contextPath=null;
// System.out.println("AJP: CP=" + contextPath);
if( contextPath!= null ){
context=contextM.getContext( contextPath );
}
// System.out.println("AJP: context=" + context );
servletName = ajpin.readString(null);
//Servlet
// System.out.println("AJP: servlet=" + servletName );
serverName = ajpin.readString(null); //Server hostname
// System.out.println("AJP: serverName=" + serverName );
dummy = ajpin.readString(null); //Apache document root
pathInfo = ajpin.readString(null); //Apache parsed
path-info
// System.out.println("AJP: PI=" + pathInfo );
// XXX Bug in mod_jserv !!!!!
pathTranslated = ajpin.readString(null); //Apache parsed
path-translated
// System.out.println("AJP: PT=" + pathTranslated );
queryString = ajpin.readString(null); //query string
// System.out.println("AJP: QS=" + queryString );
remoteAddr = ajpin.readString(""); //remote address
// System.out.println("AJP: RA=" + remoteAddr );
remoteHost = ajpin.readString(""); //remote host
// System.out.println("AJP: RH=" + remoteHost );
remoteUser = ajpin.readString(null); //remote user
// System.out.println("AJP: RU=" + remoteUser);
authType = ajpin.readString(null); //auth type
// System.out.println("AJP: AT=" + authType);
dummy = ajpin.readString(null); //remote port
method = ajpin.readString(null); //request method
// System.out.println("AJP: Meth=" + method );
requestURI = ajpin.readString(""); //request uri
//Jun Inamori modified;
if(contextPath!=null && contextPath.length()>0){
if(contextPath.equals("engine") && servletName!=null){
if(queryString!=null){
requestURI="/"+contextPath+"/"+servletName+"?"+queryString;
}
else{
requestURI="/"+contextPath+"/"+servletName;
}
}
else if(!requestURI.startsWith("/"+contextPath)){
int index=requestURI.indexOf("/",1);
if(index>0){
String original_lookup=requestURI.substring(index);
if(queryString!=null){
requestURI="/"+contextPath+original_lookup+"?"+queryString;
}
else{
requestURI="/"+contextPath+original_lookup;
if(pathInfo==null && !requestURI.endsWith("/")){
if(servletName==null){
requestURI=requestURI+"/";
pathInfo="/";
}
else if(!requestURI.endsWith(servletName)){
requestURI=requestURI+"/";
pathInfo="/";
}
}
}
}
else{
if(queryString!=null){
requestURI="/"+contextPath+"?"+queryString;
}
else{
requestURI="/"+contextPath;
if(pathInfo==null && !requestURI.endsWith("/")){
requestURI=requestURI+"/";
pathInfo="/";
}
}
}
}
}
// System.out.println("AJP: URI: " + requestURI + " CP:" + contextPath
+ " LP: " + lookupPath);
if(contextPath!=null && contextPath.length() >0 )
lookupPath=requestURI.substring( contextPath.length() + 1 );
// System.out.println("AJP: URI: " + requestURI + " CP:" + contextPath
+ " LP: " + lookupPath);
dummy = ajpin.readString(null); //script filename
// System.out.println("AJP: Script filen=" + dummy);
dummy = ajpin.readString(null); //script name
// System.out.println("AJP: Script name=" + dummy);
serverName = ajpin.readString(""); //server name
// System.out.println("AJP: serverName=" + serverName );
try {
serverPort = Integer.parseInt(ajpin.readString("80")); //server
port
} catch (Exception any) {
serverPort = 80;
}
dummy = ajpin.readString(""); //server protocol
// System.out.println("AJP: Server proto=" + dummy);
dummy = ajpin.readString(""); //server signature
// System.out.println("AJP: Server sign=" + dummy);
dummy = ajpin.readString(""); //server software
// System.out.println("AJP: Server softw=" + dummy);
jvmRoute = ajpin.readString(""); //JSERV ROUTE
if(jvmRoute.length() == 0) {
jvmRoute = null;
}
// System.out.println("AJP: Server jvmRoute=" + jvmRoute);
/**
* The two following lines are commented out because we
don't
* want to depend on unreleased versions of the jserv
module.
* - costin
*/
// dummy = ajpin.readString("");
//SSL_CLIENT_DN
// dummy = ajpin.readString("");
//SSL_CLIENT_IDN
// XXX all dummy fields will be used after core is changed to make use
// of them!
break;
case 3: // Header
token1 = ajpin.readString(null);
token2 = ajpin.readString("");
headers.putHeader(token1.toLowerCase(), token2);
break;
case 254: // Signal
signal = ajpin.read();
if (signal == 0) { // PING implemented as signal
//Jun Inamori modified
isPing=true;
try {
// close the socket connection after we send reply
socket.getOutputStream().write(0); // PING reply
sin.close();
} catch (IOException ignored) {
System.err.println(ignored);
}
} else {
try {
// close the socket connection before handling any signal
sin.close();
if ( signal== 15 ) {
// Shutdown - probably apache was stoped with apachectl stop
contextM.stop();
// same behavior as in past, because it seems that
// stopping everything doesn't work - need to figure
// out what happens with the threads ( XXX )
System.exit(0);
shutdown=true;
return;
}
} catch (Exception ignored) {
System.err.println(ignored);
}
System.err.println("Signal ignored: " + signal);
}
return;
case 4:
case 255:
return;
case -1:
throw new java.io.IOException("Stream closed prematurely");
default:
throw new java.io.IOException("Stream broken");
} // while
} // switch
} catch (IOException ioe) {
throw ioe;
} catch (Exception e) {
System.err.println("Uncaught exception" + e);
e.printStackTrace();
}
// REQUEST_URI includes query string
int idQ= requestURI.indexOf("?");
if ( idQ > -1) {
requestURI = requestURI.substring(0, idQ);
}
// System.out.println("Request: " + requestURI );
// System.out.println("Query: " + queryString );
// System.out.println("ENV: " + env_vars );
// System.out.println("HEADERS: " + headers_in );
// System.out.println("PARAMETERS: " + parameters );
//processCookies();
contentLength = headers.getIntHeader("content-length");
contentType = headers.getHeader("content-type");
// XXX
// detect for real whether or not we have more requests
// coming
// XXX
// Support persistent connection in AJP21
//moreRequests = false;
}
}
// Ajp use Status: instead of Status
class AJP12ResponseAdapter extends HttpResponseAdapter {
/** Override setStatus
*/
protected void sendStatus( int status, String message) throws
IOException {
statusSB.setLength(0);
statusSB.append("Status: " ).append( status ).append("\r\n");
sout.write(statusSB.toString().getBytes());
}
}
class Ajpv12InputStream extends BufferedInputStream {
// UTF8 is a strict superset of ASCII.
final static String CHARSET = "UTF8";
public Ajpv12InputStream(InputStream in) {
super(in);
}
public Ajpv12InputStream(InputStream in, int bufsize) {
super(in,bufsize);
}
public int readWord() throws java.io.IOException {
int b1 = read();
if( b1 == -1)
return -1;
int b2 = read();
if ( b2==-1)
return -1;
return ((int)((b1 << 8) | b2)) & 0xffff;
}
public String readString(String def) throws java.io.IOException {
int len = readWord();
if( len == 0xffff)
return def;
if( len == -1)
throw new java.io.IOException("Stream broken");
byte[] b = new byte[len];
int p = 0;
int r;
while(p<len) {
r = read(b,p, len - p);
if( r< 0) {
throw new java.io.IOException("Stream broken, couldn't
demarshal string :"+len+":"+p);
}
p = p+r;
}
return new String(b, CHARSET);
}
}
*** Source of ***
*** org.apache.tomcat.startup.TomcatJServ ***
*** Start of source code ***
/**
* TomcatJServ.java
*
* Created: Tue Mar 28 17:39:03 2000
* $Revision$
* Modified: $Date$
*
* Author: Jun Inamori
* E-mail: j-office@osa.att.ne.jp
*
* Copyright (c) 1998-2000 Jun Inamori
* 3-25-2-301 Futaba Shinagawa-ku, Tokyo, Japan.
* All rights reserved.
*
*/
package org.apache.tomcat.startup;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.tomcat.core.*;
import org.apache.tomcat.util.xml.*;
public class TomcatJServ extends HttpServlet{
/**
* Start up Tomcat.
* <p>
* The command line usage is the following:
* <pre>java org.apache.tomcat.startup.TomcatJServ</pre>
* The configuration file is assumed to be
$(TOMCAT_HOME)/conf/server.xml
* @param arguments is always ignored
*/
public static void main(String[] args) {
if(args!=null){
for(int i=0; i<args.length; i++){
System.err.println("Jun:Arg["+(i+1)+"]="+args);
}
}
try {
Tomcat tomcat=new Tomcat();
String[] arg={"hello"};
tomcat.execute( arg );
} catch(Exception ex ) {
System.err.println("FATAL: " + ex );
ex.printStackTrace();
}
}
private static final String configFile="conf/server.xml";
private static File conf_xml=null;
private static ContextManager cm=null;
private static final String SINGLE_COLUMN = "***";
private static final String STATUS_SERVLET =
"org.apache.jserv.JServ";
/**
* Defines the name of the package.
*/
public static final String PACKAGE = "TomcatJServ";
/**
* Defines the current version of the package.
*/
public static final String VERSION = "3.1Beta";
public final static String version() {
return PACKAGE + "/" + VERSION;
}
public final static String[] colors = { "#f0f0f0", "#e0e0e0" };
private static Hashtable table = new Hashtable();
private ServletOutputStream out=null;
/**
* TomcatJServ is also a servlet that returns info to the client
about its
* status. Security checks are performed by the Apache side.
* <p>
* This servlet is used as installation and configuration feedback,
since
* its correct functioning tells the user TomcatJServ is correctly
working.
* Since this class will always be in the executing JVM's classpath,
* the user receive this output if Apache and TomcatJServ are setup
correctly,
* forgetting about the servletzone configurations that may be done
* later on.
*
* @exception ServletException when servlet request fails
*/
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
out = res.getOutputStream();
// Send graphic header (banner, ...)
sendHead(out);
Tomcat tomcat=new Tomcat();
cm=new ContextManager();
conf_xml=new File(cm.getHome(), configFile);
XmlMapper xh=new XmlMapper();
xh.setDebug( 0 );
tomcat.setHelper( xh );
tomcat.setConnectorHelper( xh );
tomcat.setLogHelper( xh );
try {
xh.readXml(conf_xml,cm);
cm.init();
} catch( Exception ex ) {
sendError(out,ex);
sendTail(out);
return;
}
// Dispatch query to the right handler
try {
String query = req.getQueryString();
if(query==null){
showMenu(out);
}
else{
if (query.startsWith("status")) {
handleStatus(out, query);
} else if (query.startsWith("context")) {
handleContexts(out, query);
} else if (query.startsWith("servlet")) {
handleServlets(out, query);
} else {
showMenu(out);
}
}
} catch (Exception ex) {
sendError(out,ex);
showMenu(out);
}
// Send graphic tail (links, copyright, ...)
sendTail(out);
}
private void sendHead(ServletOutputStream out) throws IOException {
out.println("<html>");
out.println("<head>");
out.println("<meta name=\"GENERATOR\" CONTENT=\"" +
TomcatJServ.version() + "\">");
out.println("<title>" + TomcatJServ.version() + "
status</title>");
out.println("</head>");
out.println("<body bgcolor=\"#ffffff\" text=\"#000000\">");
out.println("<p align=center><img
src=\"../../status?image\"></p>");
}
private void sendTail(ServletOutputStream out) throws IOException {
out.println("<br><center><a href=\"../../status?menu\">Back to
menu</a></center>");
out.println("<p align=\"center\"><font size=-1>");
out.println("Copyright (c) 1997-99 <a
href=\"http://java.apache.org\">");
out.println("The Java Apache Project</a>.<br>");
out.println("All rights reserved.");
out.println("</font></p>");
out.println("</body>");
out.println("</html>");
}
private void showMenu(ServletOutputStream out) throws IOException {
// Write menu table
table.clear();
table.put("<a href=./" + STATUS_SERVLET +
"?status>"+"<big>Current Status</big></a>",SINGLE_COLUMN);
writeTable(out, TomcatJServ.version() + " Servlet Engine
Status", table, colors);
// Write Servlet Contexts Table
table.clear();
Enumeration contexts =cm.getContextNames();
while(contexts.hasMoreElements()) {
String name = (String) contexts.nextElement();
if(name.equals("")){
table.put("<a href=./" + STATUS_SERVLET +
"?context=><big>/</big></a>", SINGLE_COLUMN);
}
else{
table.put("<a href=./" + STATUS_SERVLET + "?context="+ name +
"><big>" + name + "</big></a>", SINGLE_COLUMN);
}
}
writeTable(out, "Servlet Contexts", table, colors);
}
private void handleStatus(ServletOutputStream out, String query)
throws IOException
{
// Write Operating System Table
table.clear();
try {
table.put("Name", System.getProperty("os.name"));
} catch (Exception ignoreItem) {};
try {
table.put("Architecture", System.getProperty("os.arch"));
} catch (Exception ignoreItem) {};
try {
table.put("Version", System.getProperty("os.version"));
} catch (Exception ignoreItem) {};
writeTable(out, "Operating System", table, colors);
// Write Java Virtual Machine Table
table.clear();
try {
table.put("Version", System.getProperty("java.version"));
} catch (Exception ignoreItem) {};
try {
table.put("Vendor", "<a
href=\""+System.getProperty("java.vendor.url")+"\">"+System.getProperty("java.vendor")+"</a>");
} catch (Exception ignoreItem) {};
try {
table.put("JVM Compiler", System.getProperty("java.compiler"));
} catch (Exception ignoreItem) {};
try {
table.put("Class Format
Version",System.getProperty("java.class.version"));
} catch (Exception ignoreItem) {};
try {
table.put("Class Path", new
StringTokenizer(System.getProperty("java.class.path"),System.getProperty("path.separator")));
} catch (Exception ignoreItem) {};
writeTable(out, "Java Virtual Machine", table, colors);
// Write General TomcatJServ Configurations Table
table.clear();
table.put("Home",cm.getHome());
table.put("WorkDir",cm.getWorkDir());
writeTable(out, "General Configurations",table,colors);
InputSource input;
Parser parser=null;
try {
if(System.getProperty("org.xml.sax.parser")!=null)
parser=ParserFactory.makeParser();
else
parser=ParserFactory.makeParser("com.sun.xml.parser.Parser");
input=new InputSource( new FileReader(conf_xml));
TomcatConf t_conf=new TomcatConf();
parser.setDocumentHandler(t_conf);
parser.setErrorHandler(t_conf);
parser.parse(input);
} catch(Exception ex) {
sendError(out,ex);
return;
}
}
private void handleContexts(ServletOutputStream out, String query)
throws IOException
{
StringTokenizer t = new StringTokenizer(query, "=");
t.nextToken();
String context="";
if(t.hasMoreTokens()){
context=t.nextToken();
}
// Write Servlet Context Configurations Table
Context con=cm.getContext(context);
if(con==null){
out.println("<h3 align=center>Error, context \"<b>"+ context +
"</b>\" was not found.</h3>");
showMenu(out);
return;
}
table.clear();
String v=con.getDescription();
if(v!=null){
table.put("Description",v);
}
v=con.getDocBase();
if(v!=null){
table.put("DocBase",v);
}
if(con.getReloadable()){
table.put("Reloadable","true");
}
else{
table.put("Reloadable","false");
}
ServletWrapper sw=con.getDefaultServlet();
if(sw!=null){
v=sw.getServletName();
table.put("DefaultServlet",v);
}
v=con.getAuthMethod();
if(v!=null){
table.put("AuthenticationMethod",v);
}
String taglibs="";
Enumeration tags=con.getTaglibs();
while(tags.hasMoreElements()) {
String name=(String)tags.nextElement();
taglibs+=name+" ";
}
if(!taglibs.equals("")){
table.put("Taglibs",taglibs);
}
int s_time=con.getSessionTimeOut();
table.put("SessionTimeOut",Integer.toString(s_time));
String welcomes="";
Enumeration wel=con.getWelcomeFiles();
while(wel.hasMoreElements()) {
String name=(String)wel.nextElement();
welcomes+=name+" ";
}
if(!welcomes.equals("")){
table.put("WelcomeFiles",welcomes);
}
writeTable(out, "Configuration of Context:<font color=#ff0000>" +
context+ "</font>",table,colors);
table.clear();
Enumeration names=con.getServletNames();
while(names.hasMoreElements()) {
String name=(String)names.nextElement();
table.put("<a
href=./"+STATUS_SERVLET+"?servlet="+name+"&context="+context+"><big>"+name+"</big></a>",SINGLE_COLUMN);
}
writeTable(out, "The following servlets are available in Context:<font
color=#ff0000>" + context+ "</font>",table,colors);
}
private void handleServlets(ServletOutputStream out, String query)
throws IOException
{
StringTokenizer t = new StringTokenizer(query, "&");
String servlet_p=t.nextToken();
if(!t.hasMoreTokens()){
out.println("<h3 align=center>Error, parameter \"<b>"+ query +
"\"</b> is not valid.</h3>");
showMenu(out);
return;
}
String context_p=t.nextToken();
StringTokenizer servlet_t=new StringTokenizer(servlet_p, "=");
servlet_t.nextToken();
if(!servlet_t.hasMoreTokens()){
out.println("<h3 align=center>Error, parameter \"<b>"+ query +
"\"</b> is not valid.</h3>");
showMenu(out);
return;
}
String servlet=servlet_t.nextToken();
StringTokenizer context_t=new StringTokenizer(context_p, "=");
context_t.nextToken();
String context="";
if(context_t.hasMoreTokens()){
context=context_t.nextToken();
}
// Write Servlet Context Configurations Table
Context con=cm.getContext(context);
if(con==null){
out.println("<h3 align=center>Error, context \"<b>"+ context +
"\"</b> was not found.</h3>");
showMenu(out);
return;
}
ServletWrapper sw=con.getServletByName(servlet);
if(sw==null){
out.println("<h3 align=center>Error, servlet \"<b>"+servlet+"\"</b>
was not found in Context: "+context+".</h3>");
String con_qu="context="+context;
handleContexts(out,con_qu);
return;
}
table.clear();
String v=sw.getServletDescription();
if(v!=null){
table.put("Description",v);
}
v=sw.getServletClass();
if(v!=null){
table.put("Class",v);
}
v=sw.getPath();
if(v!=null){
table.put("Path",v);
}
writeTable(out, "Information about Servlet:<font
color=#ff0000>"+servlet+"</font> in Context:"+context,table,colors);
}
private void writeTable(ServletOutputStream out, String
name,Hashtable table,String[] rowColors)
throws IOException {
Enumeration names = table.keys();
out.println("<p><center><table bgcolor=#000000 border=0
cellpadding=0 cellspacing=0 width=50%>");
out.println("<tr><td><table border=0 cellpadding=4 cellspacing=2
width=100%>");
out.println("<tr><td align=right valign=middle colspan=2
bgcolor=#c0c0c0>");
out.println("<h3>" + name + "</td></tr>");
for (int i = 0, j = 0; names.hasMoreElements(); i++) {
Object key = names.nextElement();
Object value = table.get(key);
try {
out.println("<tr><td align=right ");
if (value.equals(SINGLE_COLUMN)) {
out.println("colspan=2 ");
}
out.println("bgcolor=" + rowColors[j & 1] + ">");
out.println("<font size=-1>" + key.toString() +
"</font></td>");
if (!value.equals(SINGLE_COLUMN)) {
out.println("<td align=left bgcolor="+rowColors[j &
1] + ">");
if (value instanceof Enumeration) {
Enumeration e = (Enumeration) value;
while (e.hasMoreElements()) {
out.println("<b><font
size=-1>"+e.nextElement().toString() + "</font></b>");
}
} else {
out.println("<b><font size=-1>" +
value.toString()+ "</font></b>");
}
out.println("</td></tr>");
}
j++;
} catch (Exception valueIgnored) {}
}
out.println("</table></td></tr></table></center></p>");
}
private void sendError(ServletOutputStream out, Exception ex)
throws IOException {
out.println("<FONT COLOR=red>Sorry, but exception
occured.</FONT><BR><PRE>");
ex.printStackTrace(new PrintStream(out));
out.println("</PRE><BR>");
}
class TomcatConf extends HandlerBase{
int con_count=0;
int log_count=0;
public void startElement(String e_name,AttributeList list)
throws SAXException{
try{
if(e_name.equals("Connector")){
table.clear();
con_count++;
if(con_count==1){
out.println("The following connectors are available.");
}
String c_name=list.getValue("className");
table.put("ClassName",c_name);
}
if(e_name.equals("Parameter")){
String name=list.getValue("name");
String value=list.getValue("value");
table.put(name,value);
}
if(e_name.equals("Logger")){
table.clear();
log_count++;
if(log_count==1){
out.println("The following loggers are available.");
}
int att_count=list.getLength();
for(int ac=0; ac<att_count; ac++){
String name=list.getName(ac);
String value=list.getValue(ac);
table.put(name,value);
}
writeTable(out,"Logger No"+(log_count),table,colors);
}
}
catch(Exception ex){
throw (new SAXException(ex));
}
}
public void endElement(String e_name)
throws SAXException{
try{
if(e_name.equals("Connector")){
writeTable(out,"Connector No"+(con_count),table,colors);
}
}
catch(Exception ex){
throw (new SAXException(ex));
}
}
}
} // TomcatJServ
requestMap, InvokerServer, and mod_jserv (Was: Hello!)
Posted by Paul Frieden <pf...@dchain.com>.
Hello,
Ironically I had been working on S2 yesterday afternoon. My goal was to
allow mapping a particular context straight into an Apache virtual host
so I could use http://host/servlet/servletname. I started to apply your
changes, but it does a few things that don't look to be correct. I
assume that getRequestURI should return the actual requested URI, but
with these changes, the requested URI will be changed. That leaves us
with a problem of finding the right context to use. It also appends the
query string, which according to the javadoc isn't supposed to be
included in the request URI.
I was looking at the problem, and a new RequestInterceptor seemed the
easiest way besides modifying SimpleMapper. I've got this almost
working, and it works fine with servlets and JSP pages except for
getPathInfo(). In InvokerServlet.service(), the path info is modified
based on the request URI even though we already have the correct path
info. This is fine if the request URI is prefixed by the context path.
However, if it is not it will wind up mangling path info.
newPathInfo = requestPath.substring(
context.getPath().length() +
newServletPath.length(),
requestPath.length());
I looked at both Ajp23 and Http, and neither of them are configuring
path info even though they extend RequestImpl. That forces the
InvokerServlet and other code to try to decipher these based on the
request URI.
Shouldn't connectors that extend RequestImpl determine their own
pathInfo? If they don't have it explicitly passed, they could determine
it from the request URI. That would allow those that are connected to a
web server to simply pass in the information. If people are interested
I could work on the code in my spare time.
Paul Frieden
Jun Inamori wrote:
...
> S2. The solution for P2 above:
> While the JServ module sends the appropriate request to Tomcat server,
> 'Ajp12ConnectionHandler' tries to parse the original request uri to find
> the context and the servlet wrapper. For example, when the original
> request to the Apache http server is
> 'http://myhost.com/myexample/myservlet' and the path '/myexample' is
> mapped to the context '/example' on the JServ module, the JServ module
> dispatches the following request to the Tomcat server:
> ContextName: example
> ServletName: myservlet
> RequestURI: /myexample/myservlet
> Note that the context name is removed of the first slash character, i.e.
> it is 'example' and not '/example'.
> This means that 'readNextRequest' method of 'Ajp12ConnectionHandler'
> fails to fined the actual context and 'service' method of
> 'ContextManager' tells all the interceptors to parse the request uri
> (/myexample/myservlet) to get the actual contexts and the servlet
> wrappers. But, of course, the context '/myexample' does not exist in the
> ContextManager.
> The solution is to provide 'AJP12RequestAdapter' with the request uri
> suitable for retrieving both of the context name and the servlet name.
> Yes, while the request uri must be '/myexample/myservlet' on the JServ
> module side, it must be '/example/myservle' on the Tomcat server side.
> The following are the quotes from my modified source:
>
> protected void readNextRequest() throws IOException {
> --- ignored ---
> marker = ajpin.read();
> switch (marker) {
> --- ignored ---
> case 1: //beginning of request
> --- ignored ---
> requestURI = ajpin.readString(""); //request uri
> //Jun Inamori modified;
> // We need to fix the request URI here.
> if(contextPath!=null && contextPath.length()>0){
> // The context of 'engine' means that
> // the request is dispatched from
> // 'jserv-status' function.
> if(contextPath.equals("engine") && servletName!=null){
> if(queryString!=null){
> requestURI="/"+contextPath+"/"+servletName+"?"+queryString;
> }
> else{
> requestURI="/"+contextPath+"/"+servletName;
> }
> }
> else if(!requestURI.startsWith("/"+contextPath)){
> int index=requestURI.indexOf("/",1);
> if(index>0){
> String original_lookup=requestURI.substring(index);
> if(queryString!=null){
> requestURI="/"+contextPath+original_lookup+"?"+queryString;
> }
> else{
> requestURI="/"+contextPath+original_lookup;
> if(pathInfo==null && !requestURI.endsWith("/")){
> if(servletName==null){
> requestURI=requestURI+"/";
> pathInfo="/";
> }
> else if(!requestURI.endsWith(servletName)){
> requestURI=requestURI+"/";
> pathInfo="/";
> }
> }
> }
> }
> else{
> if(queryString!=null){
> requestURI="/"+contextPath+"?"+queryString;
> }
> else{
> requestURI="/"+contextPath;
> if(pathInfo==null && !requestURI.endsWith("/")){
> requestURI=requestURI+"/";
> pathInfo="/";
> }
> }
> }
> }
> }
>
> --- ignored ---
> }
>