1 /*
* Copyright 2006 Rui Damas <rui.damas at gmail com>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package contrib.winstone;
import java.sql.*;
import java.util.*;
import winstone.*;
/**
* A JDBC authentication realm to be used with Winstone Servelet container.
* <p>
* --JDBCRealm.url and --JDBCRealm.user are required.
* </p>
*
* @author Rui Damas
*/
22 public class JDBCRealm implements AuthenticationRealm {
// Command line arguments prefix
25 public static String ARGS = "JDBCRealm.";
// Command line arguments for connecting
28 public static String
ARGS_DRIVER = ARGS + "driver",
ARGS_URL = ARGS + "url",
ARGS_USER = ARGS + "user",
ARGS_PASSWORD = ARGS + "password";
// Command line arguments to SQL identifiers
35 public static String
ARGS_USER_REL = ARGS + "userRel",
ARGS_USER_NAME_COL = ARGS + "userNameCol",
ARGS_USER_CRED_COL = ARGS + "userCredCol",
ARGS_USER_ROLE_REL = ARGS + "userRoleRel",
ARGS_ROLE_NAME_COL = ARGS + "roleNameCol";
// Defaults for SQL identifiers
43 public static String
DEFAULT_USER_REL = "web_users",
DEFAULT_USER_NAME_COL = "username",
DEFAULT_USER_CRED_COL ="credential",
DEFAULT_USER_ROLE_REL = "web_user_roles",
DEFAULT_ROLE_NAME_COL = "rolename";
50 private Connection connection;
52 private final String url, user, password,
retriveUserQuery, authenticationQueryPostfix, userRolesQuery;
/**
Creates a new instance of JDBCAuthenticationRealm.
<p>
If a <code>"JDBCRealm.driver"</code> exists in the <code>args</code>
map an atempt to load the class will be made and
a success message will be printed to <code>System.out</code>,
or, if the class fails to load,
an error message will be printed to <code>System.err</code>.
</p>
*/
65 public JDBCRealm( Set rolesAllowed, Map<String, String> args ) {
// Get connection arguments
String driver = args.get( ARGS_DRIVER ),
url = args.get( ARGS_URL ),
user = args.get( ARGS_USER ),
password = args.get( ARGS_PASSWORD );
this.url = url;
this.user = user;
this.password = password;
// Get SQL identifier arguments
String userRel = args.get( ARGS_USER_REL ),
userNameCol = args.get( ARGS_USER_NAME_COL ),
userCredCol = args.get( ARGS_USER_CRED_COL ),
userRoleRel = args.get( ARGS_USER_ROLE_REL ),
roleNameCol = args.get( ARGS_ROLE_NAME_COL );
// Get defaults if necessary
if ( userRel == null ) userRel = DEFAULT_USER_REL;
if ( userNameCol == null ) userNameCol = DEFAULT_USER_NAME_COL;
if ( userCredCol == null ) userCredCol = DEFAULT_USER_CRED_COL;
if ( userRoleRel == null ) userRoleRel = DEFAULT_USER_ROLE_REL;
if ( roleNameCol == null ) roleNameCol = DEFAULT_ROLE_NAME_COL;
retriveUserQuery =
"SELECT 1\n" +
" FROM \"" + userRel + "\"\n" +
" WHERE \"" + userNameCol + "\" = ?";
// Prepare query prefixes
authenticationQueryPostfix =
"\n AND \"" + userCredCol + "\" = ?";
userRolesQuery =
"SELECT \"" + roleNameCol + "\"\n" +
" FROM \"" + userRoleRel + "\"\n" +
" WHERE \"" + userNameCol + "\" = ?";
// If the driver was specified
if ( driver != null )
try {
// Try to load the driver
Class.forName( driver );
// and notify if loaded
System.out.println( "JDBCRealm loaded jdbc driver: " + driver );}
catch ( ClassNotFoundException cnfe ) {
// Notify if fails
System.err.println(
"JDBCRealm failed to load jdbc driver: "+ driver );}
}
117 public AuthenticationPrincipal getPrincipal
118 ( String userName, String password, boolean usePassword ) {
try {
// Get a connection
if ( ( connection == null ) || connection.isClosed( ) )
connection = DriverManager.getConnection( url, user, password );
// Query for user
String query = retriveUserQuery;
if ( usePassword ) query = query + authenticationQueryPostfix;
PreparedStatement ps = connection.prepareStatement( query );
ps.setString( 1, userName );
if ( usePassword ) ps.setString( 2, password );
ResultSet resultSet = ps.executeQuery( );
// If there is a user ( row )
if ( resultSet.next( ) ) {
// Query for the user roles
query = userRolesQuery;
ps = connection.prepareStatement( query );
ps.setString( 1, userName );
resultSet = ps.executeQuery( );
// Load list
List<String> roles = new Vector<String>( );
while ( resultSet.next( ) )
roles.add( resultSet.getString( 1 ) );
return new AuthenticationPrincipal( userName, password, roles );
}
}
catch ( SQLException sqle ) {sqle.printStackTrace( );}
return null;
}
/**
* Authenticate the user - do we know them ? Return a distinct id once we
* know them.
* @return <code>getPrincipal( userName, password, true );</code>
*/
153 public AuthenticationPrincipal authenticateByUsernamePassword
154 ( String userName, String password ) {
return getPrincipal( userName, password, true );
}
/**
* Retrieve an authenticated user
* @return <code>getPrincipal( userName, password, false );</code>
*/
162 public AuthenticationPrincipal retrieveUser( String userName ) {
return getPrincipal( userName, null, false );
}
}
1 package winstone.nio;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
/**
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: NioSocketServer.java, v 1.1 2006/08/27 14:22:32 rickknowles Exp $
*/
23 public class NioSocketServer implements Runnable {
private final static int LISTEN_PORT = 6475;
27 private Thread thread;
28 private Selector selector;
30 private ServerSocket serverSocket;
32 public NioSocketServer( boolean useNIO ) throws IOException {
if ( useNIO ) {
ServerSocketChannel ssc = ServerSocketChannel.open( );
ssc.configureBlocking( false );
ServerSocket ss = ssc.socket( );
ss.bind( new InetSocketAddress( LISTEN_PORT ) );
this.selector = Selector.open( );
ssc.register( this.selector, SelectionKey.OP_ACCEPT );
} else {
this.serverSocket = new ServerSocket( LISTEN_PORT );
this.serverSocket.setSoTimeout( 500 );
}
this.thread = new Thread( this );
this.thread.setDaemon( true );
this.thread.start( );
}
51 public void run( ) {
boolean interrupted = false;
while ( !interrupted ) {
try {
if ( this.selector != null ) {
nioLoop( );
} else {
jioLoop( );
}
interrupted = Thread.interrupted( );
} catch ( IOException err ) {
err.printStackTrace( );
interrupted = true;
}
}
this.thread = null;
}
69 private void nioLoop( ) throws IOException {
this.selector.select( 500 );
Set selectedKeys = this.selector.selectedKeys( );
Iterator i = selectedKeys.iterator( );
while ( i.hasNext( ) ) {
SelectionKey key = ( SelectionKey ) i.next( );
if ( key.isAcceptable( ) ) {
ServerSocketChannel ssc = ( ServerSocketChannel ) key.channel( );
SocketChannel sc = ssc.accept( );
sc.configureBlocking( false );
sc.register( this.selector, SelectionKey.OP_READ );
} else if ( key.isReadable( ) ) {
SocketChannel sc = ( SocketChannel ) key.channel( );
ByteBuffer buffer = ByteBuffer.allocate( 10 );
buffer.clear( );
sc.read( buffer );
buffer.flip( );
sc.write( buffer );
sc.close( );
}
i.remove( );
}
}
93 private void jioLoop( ) throws IOException {
Socket socket = null;
try {
socket = this.serverSocket.accept( );
} catch ( SocketTimeoutException err ) {
}
if ( socket != null ) {
InputStream in = socket.getInputStream( );
int pos = 0;
int read = 0;
byte buffer[] = new byte[10];
while ( ( pos < buffer.length ) && ( ( read = in.read( buffer, pos, buffer.length - pos ) ) != -1 ) ){
pos += read;
}
OutputStream out = socket.getOutputStream( );
out.write( buffer, 0, pos );
in.close( );
out.close( );
socket.close( );
}
}
115 public void stop( ) {
this.thread.interrupt( );
}
119 public static void main( String argv[] ) throws Exception {
String iterArg = argv.length > 1 ? argv[1] : "1000";
int ITERATION_COUNT = Integer.parseInt( iterArg );
boolean useNIO = argv.length > 0 && argv[0].equals( "nio" );
InetAddress LOCATION = InetAddress.getLocalHost( );
System.out.println( "Address: " + LOCATION );
NioSocketServer server = new NioSocketServer( useNIO );
Thread.sleep( 1000 );
long startTime = System.currentTimeMillis( );
byte TEST_ARRAY[] = "1234567890".getBytes( );
for ( int n = 0; n < ITERATION_COUNT; n++ ) {
byte buffer[] = new byte[TEST_ARRAY.length];
Socket socket = new Socket( LOCATION, LISTEN_PORT );
socket.setSoTimeout( 50 );
OutputStream out = socket.getOutputStream( );
out.write( TEST_ARRAY );
InputStream in = socket.getInputStream( );
int read = 0;
int pos = 0;
while ( ( pos < buffer.length ) && ( ( read = in.read( buffer, pos, buffer.length - pos ) ) != -1 ) ){
pos += read;
}
in.close( );
out.close( );
socket.close( );
// if ( !Arrays.equals( TEST_ARRAY, buffer ) ) {
// throw new RuntimeException( "in and out arrays are not equal" );
// }
if ( n % 500 == 0 ) {
System.out.println( "Completed " + n + " iterations in " +
( System.currentTimeMillis( ) - startTime ) + "ms" );
}
}
System.out.println( "Completed " + ITERATION_COUNT + " iterations in " +
( System.currentTimeMillis( ) - startTime ) + "ms" );
server.stop( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Interface definition for filter objects
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface Filter {
15 public void destroy( );
17 public void doFilter( ServletRequest request, ServletResponse response,
18 FilterChain chain ) throws java.io.IOException, ServletException;
20 public void init( FilterConfig filterConfig ) throws ServletException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Interface def for chains of filters before invoking the resource.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface FilterChain {
15 public void doFilter( ServletRequest request, ServletResponse response )
throws java.io.IOException, ServletException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Configuration for filter objects.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface FilterConfig {
15 public String getFilterName( );
17 public String getInitParameter( String name );
19 public java.util.Enumeration getInitParameterNames( );
21 public ServletContext getServletContext( );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
/**
* The base class from which all servlets extend.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
18 public abstract class GenericServlet implements Servlet, ServletConfig,
Serializable {
20 private ServletConfig config;
22 public GenericServlet( ) {
}
25 public String getInitParameter( String name ) {
return config.getInitParameter( name );
}
29 public Enumeration getInitParameterNames( ) {
return config.getInitParameterNames( );
}
33 public ServletConfig getServletConfig( ) {
return this.config;
}
37 public void init( ServletConfig config ) throws ServletException {
this.config = config;
init( );
}
42 public void init( ) throws ServletException {
}
45 public void destroy( ) {
}
48 public ServletContext getServletContext( ) {
return config.getServletContext( );
}
52 public String getServletInfo( ) {
return "";
}
56 public String getServletName( ) {
return config.getServletName( );
}
60 public void log( String msg ) {
config.getServletContext( ).log( msg );
}
64 public void log( String message, Throwable t ) {
config.getServletContext( ).log( message, t );
}
68 public abstract void service( ServletRequest req, ServletResponse res )
throws IOException, ServletException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Interface defining behaviour of servlet container dispatching of requests.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface RequestDispatcher {
15 public void forward( ServletRequest request, ServletResponse response )
throws ServletException, java.io.IOException;
18 public void include( ServletRequest request, ServletResponse response )
throws ServletException, java.io.IOException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.io.IOException;
/**
* Basic servlet interface
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface Servlet {
17 public void destroy( );
19 public ServletConfig getServletConfig( );
21 public String getServletInfo( );
23 public void init( ServletConfig config ) throws ServletException;
25 public void service( ServletRequest req, ServletResponse res )
throws IOException, ServletException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Basic servlet configuation interface
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface ServletConfig {
15 public String getInitParameter( String name );
17 public java.util.Enumeration getInitParameterNames( );
19 public ServletContext getServletContext( );
21 public String getServletName( );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.util.Enumeration;
import java.net.URL;
import java.io.InputStream;
import java.util.Set;
/**
* Models the web application concept as an interface.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface ServletContext {
20 public Object getAttribute( String name );
22 public Enumeration getAttributeNames( );
24 public String getInitParameter( String name );
26 public Enumeration getInitParameterNames( );
28 public String getServletContextName( );
30 public ServletContext getContext( String uripath );
32 public String getServerInfo( );
34 public String getMimeType( String file );
36 public int getMajorVersion( );
38 public int getMinorVersion( );
40 public RequestDispatcher getRequestDispatcher( String path );
42 public RequestDispatcher getNamedDispatcher( String name );
44 public String getRealPath( String path );
46 public URL getResource( String path ) throws java.net.MalformedURLException;
48 public InputStream getResourceAsStream( String path );
50 public Set getResourcePaths( String path );
52 public String getContextPath( );
/**
* @deprecated As of Java Servlet API 2.1, with no direct replacement.
*/
57 public Servlet getServlet( String name ) throws ServletException;
/**
* @deprecated As of Java Servlet API 2.1, with no replacement.
*/
62 public Enumeration getServletNames( );
/**
* @deprecated As of Java Servlet API 2.0, with no replacement.
*/
67 public Enumeration getServlets( );
/**
* @deprecated As of Java Servlet API 2.1, use log( String message, Throwable
* throwable ) instead.
*/
73 public void log( Exception exception, String msg );
75 public void log( String msg );
77 public void log( String message, Throwable throwable );
79 public void removeAttribute( String name );
81 public void setAttribute( String name, Object object );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
12 public class ServletContextAttributeEvent extends ServletContextEvent {
13 private String name;
15 private Object value;
17 public ServletContextAttributeEvent( ServletContext source, String name,
18 Object value ) {
super( source );
this.name = name;
this.value = value;
}
24 public String getName( ) {
return this.name;
}
28 public Object getValue( ) {
return this.value;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Listens for changes to the context attributes.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public interface ServletContextAttributeListener extends
java.util.EventListener {
16 public void attributeAdded( ServletContextAttributeEvent scab );
18 public void attributeRemoved( ServletContextAttributeEvent scab );
20 public void attributeReplaced( ServletContextAttributeEvent scab );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* The event object thrown when a servlet context change occurs.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public class ServletContextEvent extends java.util.EventObject {
/**
* Constructor
*/
18 public ServletContextEvent( ServletContext source ) {
super( source );
}
22 public ServletContext getServletContext( ) {
return ( ServletContext ) this.source;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Thrown when a change to the servletContext occurs.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public interface ServletContextListener extends java.util.EventListener {
15 public void contextDestroyed( ServletContextEvent sce );
17 public void contextInitialized( ServletContextEvent sce );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.io.PrintWriter;
import java.io.PrintStream;
/**
* Generic servlet exception
*
* @author Rick Knowles
*/
17 public class ServletException extends java.lang.Exception {
18 private Throwable rootCause;
20 public ServletException( ) {
super( );
}
24 public ServletException( String message ) {
super( message );
}
28 public ServletException( String message, Throwable rootCause ) {
this( message );
this.rootCause = rootCause;
}
33 public ServletException( Throwable rootCause ) {
this( rootCause != null ? rootCause.getMessage( ) : null );
this.rootCause = rootCause;
}
38 public Throwable getRootCause( ) {
return this.rootCause;
}
42 public void printStackTrace( PrintWriter p ) {
if ( this.rootCause != null )
this.rootCause.printStackTrace( p );
p.write( "\n" );
super.printStackTrace( p );
}
49 public void printStackTrace( PrintStream p ) {
if ( this.rootCause != null )
this.rootCause.printStackTrace( p );
p.println( "\n" );
super.printStackTrace( p );
}
56 public void printStackTrace( ) {
if ( this.rootCause != null )
this.rootCause.printStackTrace( );
super.printStackTrace( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Provides the base class for servlet request streams.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public abstract class ServletInputStream extends java.io.InputStream {
15 protected ServletInputStream( ) {
super( );
}
19 public int readLine( byte[] b, int off, int len ) throws java.io.IOException {
if ( b == null )
throw new IllegalArgumentException( "null buffer" );
else if ( len + off > b.length )
throw new IllegalArgumentException(
"offset + length is greater than buffer length" );
int positionCounter = 0;
int charRead = read( );
while ( charRead != -1 ) {
b[off + positionCounter++] = ( byte ) charRead;
if ( ( charRead == '\n' ) || ( off + positionCounter == len ) ) {
return positionCounter;
} else {
charRead = read( );
}
}
return -1;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
15 public abstract class ServletOutputStream extends OutputStream {
16 final String CR_LF = "\r\n";
18 protected ServletOutputStream( ) {
super( );
}
22 public void print( boolean b ) throws IOException {
print( "" + b );
}
26 public void print( char c ) throws IOException {
print( "" + c );
}
30 public void print( double d ) throws IOException {
print( "" + d );
}
34 public void print( float f ) throws IOException {
print( "" + f );
}
38 public void print( int i ) throws IOException {
print( "" + i );
}
42 public void print( long l ) throws IOException {
print( "" + l );
}
46 public void print( String s ) throws IOException {
write( s.getBytes( ) );
}
50 public void println( ) throws IOException {
println( "" );
}
54 public void println( boolean b ) throws IOException {
println( "" + b );
}
58 public void println( char c ) throws IOException {
println( "" + c );
}
62 public void println( double d ) throws IOException {
println( "" + d );
}
66 public void println( float f ) throws IOException {
println( "" + f );
}
70 public void println( int i ) throws IOException {
println( "" + i );
}
74 public void println( long l ) throws IOException {
println( "" + l );
}
78 public void println( String s ) throws IOException {
print( s + CR_LF );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
/**
* Base request object interface definition.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface ServletRequest {
22 public Object getAttribute( String name );
24 public Enumeration getAttributeNames( );
26 public String getCharacterEncoding( );
28 public int getContentLength( );
30 public String getContentType( );
32 public ServletInputStream getInputStream( ) throws IOException;
34 public String getLocalAddr( );
36 public Locale getLocale( );
38 public Enumeration getLocales( );
40 public String getLocalName( );
42 public int getLocalPort( );
44 public String getParameter( String name );
46 public Map getParameterMap( );
48 public Enumeration getParameterNames( );
50 public String[] getParameterValues( String name );
52 public String getProtocol( );
54 public BufferedReader getReader( ) throws IOException;
56 public String getRemoteAddr( );
58 public String getRemoteHost( );
60 public int getRemotePort( );
62 public RequestDispatcher getRequestDispatcher( String path );
64 public String getScheme( );
66 public String getServerName( );
68 public int getServerPort( );
70 public boolean isSecure( );
72 public void removeAttribute( String name );
74 public void setAttribute( String name, Object o );
76 public void setCharacterEncoding( String enc ) throws UnsupportedEncodingException;
/**
* @deprecated As of Version 2.1 of the Java Servlet API, use
* ServletContext.getRealPath( String ) instead.
*/
82 public String getRealPath( String path );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* The event thrown to request attribute listeners
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ServletRequestAttributeEvent.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
15 public class ServletRequestAttributeEvent extends ServletRequestEvent {
16 private String name;
18 private Object value;
20 public ServletRequestAttributeEvent( ServletContext sc,
21 ServletRequest request, String name, Object value ) {
super( sc, request );
this.name = name;
this.value = value;
}
27 public String getName( ) {
return this.name;
}
31 public Object getValue( ) {
return this.value;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.util.EventListener;
/**
* Interface defining request attribute listeners
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ServletRequestAttributeListener.java, v 1.2 2006/02/28 07:32:48 rickknowles Exp $
*/
17 public interface ServletRequestAttributeListener extends EventListener {
18 public void attributeAdded( ServletRequestAttributeEvent srae );
20 public void attributeRemoved( ServletRequestAttributeEvent srae );
22 public void attributeReplaced( ServletRequestAttributeEvent srae );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.util.EventObject;
/**
* Request coming into scope or out of scope event
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ServletRequestEvent.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
17 public class ServletRequestEvent extends EventObject {
18 private ServletRequest request;
20 private ServletContext context;
22 public ServletRequestEvent( ServletContext sc, ServletRequest request ) {
super( sc );
this.request = request;
this.context = sc;
}
28 public ServletRequest getServletRequest( ) {
return this.request;
}
32 public ServletContext getServletContext( ) {
return this.context;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.util.EventListener;
/**
* Listener for requests going in and out of scope
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ServletRequestListener.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
17 public interface ServletRequestListener extends EventListener {
18 public void requestDestroyed( ServletRequestEvent sre );
20 public void requestInitialized( ServletRequestEvent sre );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* Wraps a servlet request object using the decorator pattern.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
21 public class ServletRequestWrapper implements ServletRequest {
22 private ServletRequest request;
24 public ServletRequestWrapper( ServletRequest request ) {
setRequest( request );
}
28 public ServletRequest getRequest( ) {
return this.request;
}
32 public void setRequest( ServletRequest request ) {
if ( request == null ) {
throw new IllegalArgumentException( "Request was null" );
} else {
this.request = request;
}
}
40 public Object getAttribute( String name ) {
return this.request.getAttribute( name );
}
44 public Enumeration getAttributeNames( ) {
return this.request.getAttributeNames( );
}
48 public void removeAttribute( String name ) {
this.request.removeAttribute( name );
}
52 public void setAttribute( String name, Object o ) {
this.request.setAttribute( name, o );
}
56 public String getCharacterEncoding( ) {
return this.request.getCharacterEncoding( );
}
60 public void setCharacterEncoding( String enc ) throws UnsupportedEncodingException {
this.request.setCharacterEncoding( enc );
}
64 public int getContentLength( ) {
return this.request.getContentLength( );
}
68 public String getContentType( ) {
return this.request.getContentType( );
}
72 public Locale getLocale( ) {
return this.request.getLocale( );
}
76 public Enumeration getLocales( ) {
return this.request.getLocales( );
}
80 public ServletInputStream getInputStream( ) throws IOException {
return this.request.getInputStream( );
}
84 public BufferedReader getReader( ) throws IOException {
return this.request.getReader( );
}
88 public String getParameter( String name ) {
return this.request.getParameter( name );
}
92 public Map getParameterMap( ) {
return this.request.getParameterMap( );
}
96 public Enumeration getParameterNames( ) {
return this.request.getParameterNames( );
}
100 public String[] getParameterValues( String name ) {
return this.request.getParameterValues( name );
}
104 public RequestDispatcher getRequestDispatcher( String path ) {
return this.request.getRequestDispatcher( path );
}
108 public String getProtocol( ) {
return this.request.getProtocol( );
}
112 public String getRemoteAddr( ) {
return this.request.getRemoteAddr( );
}
116 public String getRemoteHost( ) {
return this.request.getRemoteHost( );
}
120 public String getScheme( ) {
return this.request.getScheme( );
}
124 public String getServerName( ) {
return this.request.getServerName( );
}
128 public int getServerPort( ) {
return this.request.getServerPort( );
}
132 public String getLocalAddr( ) {
return this.request.getLocalAddr( );
}
136 public String getLocalName( ) {
return this.request.getLocalName( );
}
140 public int getLocalPort( ) {
return this.request.getLocalPort( );
}
144 public int getRemotePort( ) {
return this.request.getRemotePort( );
}
148 public boolean isSecure( ) {
return this.request.isSecure( );
}
/**
* @deprecated
*/
155 public String getRealPath( String path ) {
return this.request.getRealPath( path );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.util.Locale;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Base response interface definition.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface ServletResponse {
19 public void flushBuffer( ) throws IOException;
21 public int getBufferSize( );
23 public void reset( );
25 public void resetBuffer( );
27 public void setBufferSize( int size );
29 public boolean isCommitted( );
31 public String getCharacterEncoding( );
33 public void setCharacterEncoding( String charset );
35 public String getContentType( );
37 public void setContentType( String type );
39 public void setContentLength( int len );
41 public Locale getLocale( );
43 public void setLocale( Locale loc );
45 public ServletOutputStream getOutputStream( ) throws IOException;
47 public PrintWriter getWriter( ) throws IOException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
import java.util.Locale;
import java.io.IOException;
/**
* Wraps a servlet response object using the decorator pattern
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
17 public class ServletResponseWrapper implements ServletResponse {
18 private ServletResponse response;
20 public ServletResponseWrapper( ServletResponse response ) {
setResponse( response );
}
24 public ServletResponse getResponse( ) {
return this.response;
}
28 public void setResponse( ServletResponse response ) {
if ( response == null ) {
throw new IllegalArgumentException( "Response was null" );
}
this.response = response;
}
35 public Locale getLocale( ) {
return this.response.getLocale( );
}
39 public void setLocale( Locale loc ) {
this.response.setLocale( loc );
}
43 public ServletOutputStream getOutputStream( ) throws IOException {
return this.response.getOutputStream( );
}
47 public java.io.PrintWriter getWriter( ) throws IOException {
return this.response.getWriter( );
}
51 public boolean isCommitted( ) {
return this.response.isCommitted( );
}
55 public int getBufferSize( ) {
return this.response.getBufferSize( );
}
59 public void setBufferSize( int size ) {
this.response.setBufferSize( size );
}
63 public void reset( ) {
this.response.reset( );
}
67 public void resetBuffer( ) {
this.response.resetBuffer( );
}
71 public void flushBuffer( ) throws IOException {
this.response.flushBuffer( );
}
75 public void setContentLength( int len ) {
this.response.setContentLength( len );
}
79 public void setContentType( String type ) {
this.response.setContentType( type );
}
83 public String getContentType( ) {
return this.response.getContentType( );
}
87 public String getCharacterEncoding( ) {
return this.response.getCharacterEncoding( );
}
91 public void setCharacterEncoding( String charset ) {
this.response.setCharacterEncoding( charset );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* This is just to ensure that a servlet can flag itself as a non-multithreaded
* instance.
*
* @deprecated
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface SingleThreadModel {
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet;
/**
* Thrown if a servlet is permanently or temporarily unavailable
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: UnavailableException.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
15 public class UnavailableException extends ServletException {
private int seconds;
18 private Servlet servlet;
/**
* @deprecated As of Java Servlet API 2.2, use UnavailableException( String,
* int ) instead.
*/
24 public UnavailableException( int seconds, Servlet servlet, String msg ) {
this( servlet, msg );
this.seconds = ( seconds <= 0 ? 0 : seconds );
}
/**
* @deprecated As of Java Servlet API 2.2, use UnavailableException( String )
* instead.
*/
33 public UnavailableException( Servlet servlet, String msg ) {
this( msg );
this.servlet = servlet;
}
/**
* Constructs a new exception with a descriptive message indicating that the
* servlet is permanently unavailable.
*/
42 public UnavailableException( String msg ) {
super( msg );
}
/**
* Constructs a new exception with a descriptive message indicating that the
* servlet is temporarily unavailable and giving an estimate of how long it
* will be unavailable.
*/
51 public UnavailableException( String msg, int seconds ) {
this( msg );
this.seconds = ( seconds <= 0 ? 0 : seconds );
}
/**
* @deprecated As of Java Servlet API 2.2, with no replacement. Returns the
* servlet that is reporting its unavailability.
*/
60 public Servlet getServlet( ) {
return this.servlet;
}
/**
* Returns the number of seconds the servlet expects to be temporarily
* unavailable.
*/
68 public int getUnavailableSeconds( ) {
return this.seconds;
}
/**
* Returns a boolean indicating whether the servlet is permanently
* unavailable.
*/
76 public boolean isPermanent( ) {
return this.seconds <= 0;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* Cookie model value object
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public class Cookie implements Cloneable {
15 private String name;
16 private String value;
17 private String comment;
18 private String domain;
19 private String path;
private boolean secure;
private int maxAge;
private int version;
24 public Cookie( String name, String value ) {
setName( name );
setValue( value );
setMaxAge( -1 );
}
30 public Object clone( ) {
Cookie clone = new Cookie( this.name, this.value );
clone.setComment( this.comment );
clone.setDomain( this.domain );
clone.setMaxAge( this.maxAge );
clone.setSecure( this.secure );
clone.setVersion( this.version );
clone.setPath( this.path );
return clone;
}
41 public String getComment( ) {
return this.comment;
}
45 public String getDomain( ) {
return this.domain;
}
49 public int getMaxAge( ) {
return this.maxAge;
}
53 public String getName( ) {
return this.name;
}
57 public String getPath( ) {
return this.path;
}
61 public boolean getSecure( ) {
return this.secure;
}
65 public String getValue( ) {
return this.value;
}
69 public int getVersion( ) {
return this.version;
}
73 private void setName( String name ) {
if ( name == null ) {
throw new IllegalArgumentException( "Cookie name was null" );
} else if ( name.indexOf( ";" ) != -1 ) {
throw new IllegalArgumentException( "Cookie name contains a semicolon" );
} else if ( name.indexOf( ", " ) != -1 ) {
throw new IllegalArgumentException( "Cookie name contains a comma" );
} else if ( name.startsWith( "$" ) ) {
throw new IllegalArgumentException( "Cookie name starts with $" );
} else {
// Check for white space, comma, semicolon
for ( int n = 0; n < name.length( ); n++ ) {
char c = name.charAt( n );
if ( c <= 0x20 || c >= 0x7f ) {
throw new IllegalArgumentException( "Cookie name contains whitespace or " +
"non-alphanumeric char: " + name.charAt( n ) + " in " + name );
}
}
this.name = name;
}
}
95 public void setComment( String purpose ) {
this.comment = purpose;
}
99 public void setDomain( String pattern ) {
this.domain = pattern;
}
103 public void setMaxAge( int expiry ) {
this.maxAge = expiry;
}
107 public void setPath( String uri ) {
this.path = uri;
}
111 public void setSecure( boolean flag ) {
this.secure = flag;
}
115 public void setValue( String newValue ) {
this.value = newValue;
}
119 public void setVersion( int v ) {
this.version = v;
}
123 public String toString( ) {
return "[Cookie: name=" + this.name + " value=" + this.value + " version=" +
this.version + " path=" + this.path + " domain=" + this.domain + " comment=" +
this.comment + " maxAge=" + this.maxAge + " secure=" + this.secure + "]";
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Base class for http servlets
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
25 public abstract class HttpServlet extends javax.servlet.GenericServlet
26 implements Serializable {
27 static final String METHOD_DELETE = "DELETE";
28 static final String METHOD_HEAD = "HEAD";
29 static final String METHOD_GET = "GET";
30 static final String METHOD_OPTIONS = "OPTIONS";
31 static final String METHOD_POST = "POST";
32 static final String METHOD_PUT = "PUT";
33 static final String METHOD_TRACE = "TRACE";
34 static final String HEADER_IFMODSINCE = "If-Modified-Since";
35 static final String HEADER_LASTMOD = "Last-Modified";
37 public HttpServlet( ) {
super( );
}
41 public void service( ServletRequest request, ServletResponse response )
throws ServletException, IOException {
if ( ( request instanceof HttpServletRequest )
&& ( response instanceof HttpServletResponse ) )
service( ( HttpServletRequest ) request,
( HttpServletResponse ) response );
else
throw new IllegalArgumentException(
"Not an Http servlet request - invalid types" );
}
52 private void notAcceptedMethod( HttpServletRequest request,
53 HttpServletResponse response, String method )
throws ServletException, IOException {
if ( request.getProtocol( ).endsWith( "1.1" ) )
response.sendError( HttpServletResponse.SC_METHOD_NOT_ALLOWED,
method + " not allowed" );
else
response.sendError( HttpServletResponse.SC_BAD_REQUEST, method
+ " not allowed" );
}
63 protected void doGet( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
notAcceptedMethod( req, resp, "GET" );
}
68 protected long getLastModified( HttpServletRequest req ) {
return -1;
}
72 protected void doPost( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
notAcceptedMethod( req, resp, "POST" );
}
77 protected void doPut( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
notAcceptedMethod( req, resp, "PUT" );
}
82 protected void doDelete( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
notAcceptedMethod( req, resp, "DELETE" );
}
87 protected void doOptions( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
notAcceptedMethod( req, resp, "OPTIONS" );
}
92 protected void doTrace( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
notAcceptedMethod( req, resp, "TRACE" );
}
97 protected void service( HttpServletRequest request,
98 HttpServletResponse response ) throws ServletException, IOException {
String method = request.getMethod( );
if ( method.equals( METHOD_GET ) ) {
long lastModified = getLastModified( request );
if ( lastModified == -1 )
doGet( request, response );
else {
long ifModifiedSince = request.getDateHeader( HEADER_IFMODSINCE );
if ( ifModifiedSince < ( lastModified / 1000 * 1000 ) ) {
if ( !response.containsHeader( HEADER_LASTMOD )
&& ( lastModified >= 0 ) )
response.setDateHeader( HEADER_LASTMOD, lastModified );
doGet( request, response );
} else
response.setStatus( HttpServletResponse.SC_NOT_MODIFIED );
}
} else if ( method.equals( METHOD_HEAD ) ) {
long lastModified = getLastModified( request );
if ( !response.containsHeader( HEADER_LASTMOD ) && ( lastModified >= 0 ) )
response.setDateHeader( HEADER_LASTMOD, lastModified );
doHead( request, response );
} else if ( method.equals( METHOD_POST ) )
doPost( request, response );
else if ( method.equals( METHOD_PUT ) )
doPut( request, response );
else if ( method.equals( METHOD_DELETE ) )
doDelete( request, response );
else if ( method.equals( METHOD_OPTIONS ) )
doOptions( request, response );
else if ( method.equals( METHOD_TRACE ) )
doTrace( request, response );
else
notAcceptedMethod( request, response, method );
}
134 protected void doHead( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
NoBodyResponse response = new NoBodyResponse( resp );
doGet( req, response );
response.setContentLength( );
}
141 class NoBodyResponse extends HttpServletResponseWrapper {
142 private NoBodyOutputStream noBody;
144 private PrintWriter writer;
private boolean contentLengthSet;
148 NoBodyResponse( HttpServletResponse mainResponse ) {
super( mainResponse );
this.noBody = new NoBodyOutputStream( );
}
153 void setContentLength( ) {
if ( !contentLengthSet )
setContentLength( this.noBody.getContentLength( ) );
}
158 public void setContentLength( int length ) {
super.setContentLength( length );
this.contentLengthSet = true;
}
163 public void setContentType( String type ) {
getResponse( ).setContentType( type );
}
167 public ServletOutputStream getOutputStream( ) throws IOException {
return noBody;
}
171 public String getCharacterEncoding( ) {
return getResponse( ).getCharacterEncoding( );
}
175 public PrintWriter getWriter( ) throws UnsupportedEncodingException {
if ( writer == null )
writer = new PrintWriter( new OutputStreamWriter( noBody,
getCharacterEncoding( ) ) );
return writer;
}
}
183 class NoBodyOutputStream extends ServletOutputStream {
private int contentLength = 0;
186 NoBodyOutputStream( ) {
}
189 int getContentLength( ) {
return contentLength;
}
193 public void write( int b ) throws IOException {
contentLength++;
}
197 public void write( byte buf[], int offset, int len ) throws IOException {
contentLength += len;
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
import java.util.Enumeration;
import java.security.Principal;
/**
* Interface definition for http requests.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
17 public interface HttpServletRequest extends javax.servlet.ServletRequest {
18 public static final String BASIC_AUTH = "BASIC";
19 public static final String CLIENT_CERT_AUTH = "CLIENT_CERT";
20 public static final String DIGEST_AUTH = "DIGEST";
21 public static final String FORM_AUTH = "FORM";
23 public String getAuthType( );
25 public String getContextPath( );
27 public Cookie[] getCookies( );
29 public long getDateHeader( String name );
31 public String getHeader( String name );
33 public Enumeration getHeaderNames( );
35 public Enumeration getHeaders( String name );
37 public int getIntHeader( String name );
39 public String getMethod( );
41 public String getPathInfo( );
43 public String getPathTranslated( );
45 public String getQueryString( );
47 public String getRemoteUser( );
49 public String getRequestedSessionId( );
51 public String getRequestURI( );
53 public StringBuffer getRequestURL( );
55 public String getServletPath( );
57 public HttpSession getSession( );
59 public HttpSession getSession( boolean create );
61 public Principal getUserPrincipal( );
63 public boolean isRequestedSessionIdFromCookie( );
65 public boolean isRequestedSessionIdFromURL( );
67 public boolean isRequestedSessionIdValid( );
69 public boolean isUserInRole( String role );
/**
* @deprecated As of Version 2.1 of the Java Servlet API, use
* isRequestedSessionIdFromURL( ) instead.
*/
75 public boolean isRequestedSessionIdFromUrl( );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
import java.util.Enumeration;
import java.security.Principal;
/**
* Wraps HttpServletRequest objects in a decorator pattern
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
17 public class HttpServletRequestWrapper extends
18 javax.servlet.ServletRequestWrapper implements HttpServletRequest {
19 private HttpServletRequest httpRequest;
21 public HttpServletRequestWrapper( HttpServletRequest request ) {
super( request );
this.httpRequest = request;
}
26 public void setRequest( javax.servlet.ServletRequest request ) {
if ( request instanceof HttpServletRequest ) {
super.setRequest( request );
this.httpRequest = ( HttpServletRequest ) request;
} else
throw new IllegalArgumentException( "Not an HttpServletRequest" );
}
34 public String getAuthType( ) {
return this.httpRequest.getAuthType( );
}
38 public String getContextPath( ) {
return this.httpRequest.getContextPath( );
}
42 public Cookie[] getCookies( ) {
return this.httpRequest.getCookies( );
}
46 public long getDateHeader( String name ) {
return this.httpRequest.getDateHeader( name );
}
50 public String getHeader( String name ) {
return this.httpRequest.getHeader( name );
}
54 public Enumeration getHeaderNames( ) {
return this.httpRequest.getHeaderNames( );
}
58 public Enumeration getHeaders( String name ) {
return this.httpRequest.getHeaders( name );
}
62 public int getIntHeader( String name ) {
return this.httpRequest.getIntHeader( name );
}
66 public String getMethod( ) {
return this.httpRequest.getMethod( );
}
70 public String getPathInfo( ) {
return this.httpRequest.getPathInfo( );
}
74 public String getPathTranslated( ) {
return this.httpRequest.getPathTranslated( );
}
78 public String getQueryString( ) {
return this.httpRequest.getQueryString( );
}
82 public String getRemoteUser( ) {
return this.httpRequest.getRemoteUser( );
}
86 public String getRequestedSessionId( ) {
return this.httpRequest.getRequestedSessionId( );
}
90 public String getRequestURI( ) {
return this.httpRequest.getRequestURI( );
}
94 public String getServletPath( ) {
return this.httpRequest.getServletPath( );
}
98 public StringBuffer getRequestURL( ) {
return this.httpRequest.getRequestURL( );
}
102 public HttpSession getSession( ) {
return this.httpRequest.getSession( );
}
106 public HttpSession getSession( boolean create ) {
return this.httpRequest.getSession( create );
}
110 public Principal getUserPrincipal( ) {
return this.httpRequest.getUserPrincipal( );
}
114 public boolean isRequestedSessionIdFromCookie( ) {
return this.httpRequest.isRequestedSessionIdFromCookie( );
}
118 public boolean isRequestedSessionIdFromURL( ) {
return this.httpRequest.isRequestedSessionIdFromURL( );
}
122 public boolean isRequestedSessionIdValid( ) {
return this.httpRequest.isRequestedSessionIdValid( );
}
126 public boolean isUserInRole( String role ) {
return this.httpRequest.isUserInRole( role );
}
/**
* @deprecated
*/
133 public boolean isRequestedSessionIdFromUrl( ) {
return this.httpRequest.isRequestedSessionIdFromUrl( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
import java.io.IOException;
/**
* Interface definition for http response objects.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
16 public interface HttpServletResponse extends javax.servlet.ServletResponse {
public static final int SC_ACCEPTED = 202;
public static final int SC_BAD_GATEWAY = 502;
public static final int SC_BAD_REQUEST = 400;
public static final int SC_CONFLICT = 409;
public static final int SC_CONTINUE = 100;
public static final int SC_CREATED = 201;
public static final int SC_EXPECTATION_FAILED = 417;
public static final int SC_FORBIDDEN = 403;
public static final int SC_FOUND = 302;
public static final int SC_GATEWAY_TIMEOUT = 504;
public static final int SC_GONE = 410;
public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
public static final int SC_INTERNAL_SERVER_ERROR = 500;
public static final int SC_LENGTH_REQUIRED = 411;
public static final int SC_METHOD_NOT_ALLOWED = 405;
public static final int SC_MOVED_PERMANENTLY = 301;
public static final int SC_MOVED_TEMPORARILY = 302;
public static final int SC_MULTIPLE_CHOICES = 300;
public static final int SC_NO_CONTENT = 204;
public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
public static final int SC_NOT_ACCEPTABLE = 406;
public static final int SC_NOT_FOUND = 404;
public static final int SC_NOT_IMPLEMENTED = 501;
public static final int SC_NOT_MODIFIED = 304;
public static final int SC_OK = 200;
public static final int SC_PARTIAL_CONTENT = 206;
public static final int SC_PAYMENT_REQUIRED = 402;
public static final int SC_PRECONDITION_FAILED = 412;
public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
public static final int SC_REQUEST_TIMEOUT = 408;
public static final int SC_REQUEST_URI_TOO_LONG = 414;
public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
public static final int SC_RESET_CONTENT = 205;
public static final int SC_SEE_OTHER = 303;
public static final int SC_SERVICE_UNAVAILABLE = 503;
public static final int SC_SWITCHING_PROTOCOLS = 101;
public static final int SC_TEMPORARY_REDIRECT = 307;
public static final int SC_UNAUTHORIZED = 401;
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
public static final int SC_USE_PROXY = 305;
59 public void addCookie( Cookie cookie );
61 public void addDateHeader( String name, long date );
63 public void addHeader( String name, String value );
65 public void addIntHeader( String name, int value );
67 public boolean containsHeader( String name );
69 public String encodeRedirectURL( String url );
71 public String encodeURL( String url );
73 public void sendError( int sc ) throws IOException;
75 public void sendError( int sc, String msg ) throws IOException;
77 public void sendRedirect( String location ) throws IOException;
79 public void setDateHeader( String name, long date );
81 public void setHeader( String name, String value );
83 public void setIntHeader( String name, int value );
85 public void setStatus( int sc );
/**
* @deprecated As of version 2.1, due to ambiguous meaning of the message
* parameter. To set a status code use setStatus( int ), to send
* an error with a description use sendError( int, String ). Sets
* the status code and message for this response.
*/
93 public void setStatus( int sc, String sm );
/**
* @deprecated As of version 2.1, use encodeRedirectURL( String url ) instead
*/
98 public String encodeRedirectUrl( String url );
/**
* @deprecated As of version 2.1, use encodeURL( String url ) instead
*/
103 public String encodeUrl( String url );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
import java.io.IOException;
/**
* Wraps HttpServletResponse objects in a decorator pattern
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
16 public class HttpServletResponseWrapper extends
17 javax.servlet.ServletResponseWrapper implements HttpServletResponse {
18 private HttpServletResponse httpResponse;
20 public HttpServletResponseWrapper( HttpServletResponse response ) {
super( response );
this.httpResponse = response;
}
25 public void setResponse( javax.servlet.ServletResponse response ) {
if ( response instanceof HttpServletResponse ) {
super.setResponse( response );
this.httpResponse = ( HttpServletResponse ) response;
} else
throw new IllegalArgumentException( "Not an HttpServletResponse" );
}
33 public void addCookie( Cookie cookie ) {
this.httpResponse.addCookie( cookie );
}
37 public void addDateHeader( String name, long date ) {
this.httpResponse.addDateHeader( name, date );
}
41 public void addHeader( String name, String value ) {
this.httpResponse.addHeader( name, value );
}
45 public void addIntHeader( String name, int value ) {
this.httpResponse.addIntHeader( name, value );
}
49 public boolean containsHeader( String name ) {
return this.httpResponse.containsHeader( name );
}
53 public String encodeRedirectURL( String url ) {
return this.httpResponse.encodeRedirectURL( url );
}
57 public String encodeURL( String url ) {
return this.httpResponse.encodeURL( url );
}
61 public void sendError( int sc ) throws IOException {
this.httpResponse.sendError( sc );
}
65 public void sendError( int sc, String msg ) throws IOException {
this.httpResponse.sendError( sc, msg );
}
69 public void sendRedirect( String location ) throws IOException {
this.httpResponse.sendRedirect( location );
}
73 public void setDateHeader( String name, long date ) {
this.httpResponse.setDateHeader( name, date );
}
77 public void setHeader( String name, String value ) {
this.httpResponse.setHeader( name, value );
}
81 public void setIntHeader( String name, int value ) {
this.httpResponse.setIntHeader( name, value );
}
85 public void setStatus( int sc ) {
this.httpResponse.setStatus( sc );
}
/**
* @deprecated
*/
92 public String encodeRedirectUrl( String url ) {
return this.httpResponse.encodeRedirectUrl( url );
}
/**
* @deprecated
*/
99 public String encodeUrl( String url ) {
return this.httpResponse.encodeUrl( url );
}
/**
* @deprecated
*/
106 public void setStatus( int sc, String sm ) {
this.httpResponse.setStatus( sc, sm );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
import java.util.Enumeration;
import javax.servlet.ServletContext;
/**
* Interface for http sessions on the server.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface HttpSession {
19 public Object getAttribute( String name );
21 public Enumeration getAttributeNames( );
23 public long getCreationTime( );
25 public String getId( );
27 public long getLastAccessedTime( );
29 public int getMaxInactiveInterval( );
31 public ServletContext getServletContext( );
33 public void invalidate( );
35 public boolean isNew( );
37 public void removeAttribute( String name );
39 public void setAttribute( String name, Object value );
41 public void setMaxInactiveInterval( int interval );
/**
* @deprecated As of Version 2.1, this method is deprecated and has no
* replacement. It will be removed in a future version of the
* Java Servlet API.
*/
48 public HttpSessionContext getSessionContext( );
/**
* @deprecated As of Version 2.2, this method is replaced by
* getAttribute( java.lang.String ).
*/
54 public Object getValue( String name );
/**
* @deprecated As of Version 2.2, this method is replaced by
* getAttributeNames( )
*/
60 public String[] getValueNames( );
/**
* @deprecated As of Version 2.2, this method is replaced by
* setAttribute( java.lang.String, java.lang.Object )
*/
66 public void putValue( String name, Object value );
/**
* @deprecated As of Version 2.2, this method is replaced by
* removeAttribute( java.lang.String )
*/
72 public void removeValue( String name );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* Interface for listeners interested in the session activation/deactivation
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public interface HttpSessionActivationListener extends java.util.EventListener {
15 public void sessionDidActivate( HttpSessionEvent se );
17 public void sessionWillPassivate( HttpSessionEvent se );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* Interface for session attribute listeners
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public interface HttpSessionAttributeListener extends java.util.EventListener {
15 public void attributeAdded( HttpSessionBindingEvent se );
17 public void attributeRemoved( HttpSessionBindingEvent se );
19 public void attributeReplaced( HttpSessionBindingEvent se );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* An object addition or removal notification
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public class HttpSessionBindingEvent extends HttpSessionEvent {
15 private String name;
17 private Object value;
19 public HttpSessionBindingEvent( HttpSession session, String name ) {
super( session );
this.name = name;
}
24 public HttpSessionBindingEvent( HttpSession session, String name,
25 Object value ) {
this( session, name );
this.value = value;
}
30 public String getName( ) {
return this.name;
}
34 public Object getValue( ) {
return this.value;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* Listener interface for listeners to the session binding events
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public interface HttpSessionBindingListener extends java.util.EventListener {
15 public void valueBound( HttpSessionBindingEvent event );
17 public void valueUnbound( HttpSessionBindingEvent event );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* Shared session context interface - deprecated
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @deprecated
*/
public abstract interface HttpSessionContext {
/**
* @deprecated
*/
19 public abstract java.util.Enumeration getIds( );
/**
* @deprecated
*/
24 public abstract HttpSession getSession( String sessionId );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* Base event class for session changed events
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public class HttpSessionEvent extends java.util.EventObject {
15 public HttpSessionEvent( HttpSession source ) {
super( source );
}
19 public HttpSession getSession( ) {
return ( HttpSession ) this.source;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
/**
* Listener events for session creation and destruction
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
14 public interface HttpSessionListener extends java.util.EventListener {
15 public void sessionCreated( HttpSessionEvent se );
17 public void sessionDestroyed( HttpSessionEvent se );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package javax.servlet.http;
import java.util.Hashtable;
import javax.servlet.ServletInputStream;
import java.util.StringTokenizer;
/**
* Generic utility functions for use in the servlet container.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
18 public class HttpUtils {
/**
* @deprecated Reconstructs the URL the client used to make the request,
* using information in the HttpServletRequest object.
*/
23 public static StringBuffer getRequestURL( HttpServletRequest req ) {
return req.getRequestURL( );
}
/**
* @deprecated Parses data from an HTML form that the client sends to the
* server using the HTTP POST method and the
* application/x-www-form-urlencoded MIME type.
*/
32 public static Hashtable parsePostData( int len, ServletInputStream in ) {
try {
byte body[] = new byte[len];
int startPos = 0;
int readChars = in.read( body, startPos, len - startPos );
while ( ( readChars != -1 ) && ( len != startPos ) ) {
startPos += readChars;
readChars = in.read( body, startPos, len - startPos );
}
if ( len != startPos )
throw new java.io.IOException( "Stream ended early" );
else {
String post = new String( body, 0, len, "8859_1" );
return parseQueryString( post );
}
} catch ( java.io.IOException err ) {
throw new IllegalArgumentException( "Error parsing request body - "
+ err.getMessage( ) );
}
}
/**
* @deprecated Parses a query string passed from the client to the server
* and builds a HashTable object with key-value pairs.
*/
57 public static Hashtable parseQueryString( String urlEncodedParams ) {
Hashtable params = new Hashtable( );
StringTokenizer st = new StringTokenizer( urlEncodedParams, "&", false );
while ( st.hasMoreTokens( ) ) {
String token = st.nextToken( );
int equalPos = token.indexOf( '=' );
if ( equalPos == -1 )
continue;
String name = token.substring( 0, equalPos );
String value = token.substring( equalPos + 1 );
String decodedName = decodeURLToken( name );
String decodedValue = decodeURLToken( value );
Object already = params.get( decodedName );
if ( already == null )
params.put( decodedName, new String[] { decodedValue } );
else if ( already instanceof String[] ) {
String alreadyArray[] = ( String[] ) already;
String oneMore[] = new String[alreadyArray.length + 1];
System.arraycopy( alreadyArray, 0, oneMore, 0,
alreadyArray.length );
oneMore[oneMore.length - 1] = decodedValue;
params.put( decodedName, oneMore );
}
}
return params;
}
85 private static String decodeURLToken( String in ) {
StringBuffer workspace = new StringBuffer( );
for ( int n = 0; n < in.length( ); n++ ) {
char thisChar = in.charAt( n );
if ( thisChar == '+' )
workspace.append( ' ' );
else if ( thisChar == '%' ) {
int decoded = Integer.parseInt( in.substring( n + 1, n + 3 ), 16 );
workspace.append( ( char ) decoded );
n += 2;
} else
workspace.append( thisChar );
}
return workspace.toString( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
/**
* Used for logging accesses, eg in Apache access_log style
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: AccessLogger.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
public interface AccessLogger {
16 public void log( String originalURL, WinstoneRequest request, WinstoneResponse response );
17 public void destroy( );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.*;
/**
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: AuthenticationHandler.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
public interface AuthenticationHandler {
/**
* Evaluates any authentication constraints, intercepting if auth is
* required. The relevant authentication handler subclass's logic is used to
* actually authenticate.
*
* @return A boolean indicating whether to continue after this request
*/
25 public boolean processAuthentication( ServletRequest request,
26 ServletResponse response, String pathRequested ) throws IOException,
ServletException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.Serializable;
import java.security.Principal;
import java.util.List;
/**
* Implements the principal method - basically just a way of identifying an
* authenticated user.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: AuthenticationPrincipal.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
20 public class AuthenticationPrincipal implements Principal, Serializable {
21 private String userName;
22 private String password;
23 private List roles;
24 private String authenticationType;
/**
* Constructor
*/
29 public AuthenticationPrincipal( String userName, String password, List roles ) {
this.userName = userName;
this.password = password;
this.roles = roles;
}
35 public String getName( ) {
return this.userName;
}
39 public String getPassword( ) {
return this.password;
}
43 public String getAuthType( ) {
return this.authenticationType;
}
47 public void setAuthType( String authType ) {
this.authenticationType = authType;
}
/**
* Searches for the requested role in this user's roleset.
*/
54 public boolean isUserIsInRole( String role ) {
if ( this.roles == null )
return false;
else if ( role == null )
return false;
else
return this.roles.contains( role );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
/**
* Interface for authentication realms.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: AuthenticationRealm.java, v 1.3 2006/12/09 03:56:41 rickknowles Exp $
*/
public interface AuthenticationRealm {
/**
* Authenticate the user - do we know them ? Return a distinct id once we
* know them. Used by the BASIC and FORM authentication methods.
*/
20 public AuthenticationPrincipal authenticateByUsernamePassword(
21 String userName, String password );
/**
* Retrieve an authenticated user. Used by the DIGEST and CLIENTCERT authentication methods.
*/
26 public AuthenticationPrincipal retrieveUser( String userName );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* Represents a cluster implementation, which is basically the communication
* mechanism between a group of winstone containers.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Cluster.java, v 1.5 2006/02/28 07:32:47 rickknowles Exp $
*/
public interface Cluster {
/**
* Destroy the maintenance thread if there is one. Prepare for shutdown
*/
25 public void destroy( );
/**
* Check if the other nodes in this cluster have a session for this
* sessionId.
*
* @param sessionId The id of the session to check for
* @param webAppConfig The web app that owns the session we want
* @return A valid session instance
*/
35 public WinstoneSession askClusterForSession( String sessionId,
36 WebAppConfiguration webAppConfig );
/**
* Accept a control socket request related to the cluster functions and
* process the request.
*
* @param requestType A byte indicating the request type
* @param in Socket input stream
* @param outSocket output stream
* @param hostConfig The collection of all local webapps
* @throws IOException
*/
48 public void clusterRequest( byte requestType, InputStream in,
49 OutputStream out, Socket socket, HostGroup hostGroup )
throws IOException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
/**
* A simple servlet that writes out the body of the error
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ErrorServlet.java, v 1.3 2006/02/28 07:32:47 rickknowles Exp $
*/
26 public class ErrorServlet extends HttpServlet {
28 public void service( ServletRequest request, ServletResponse response ) throws ServletException, IOException {
Integer sc = ( Integer ) request.getAttribute( RequestDispatcher.ERROR_STATUS_CODE );
String msg = ( String ) request.getAttribute( RequestDispatcher.ERROR_MESSAGE );
Throwable err = ( Throwable ) request.getAttribute( RequestDispatcher.ERROR_EXCEPTION );
StringWriter sw = new StringWriter( );
PrintWriter pw = new PrintWriter( sw );
if ( err != null ) {
err.printStackTrace( pw );
} else {
pw.println( "( none )" );
}
pw.flush( );
// If we are here there was no error servlet, so show the default error page
String output = Launcher.RESOURCES.getString( "WinstoneResponse.ErrorPage",
new String[] { sc + "", ( msg == null ? "" : msg ), sw.toString( ),
Launcher.RESOURCES.getString( "ServerVersion" ),
"" + new Date( ) } );
response.setContentLength( output.getBytes( response.getCharacterEncoding( ) ).length );
Writer out = response.getWriter( );
out.write( output );
out.flush( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import org.w3c.dom.Node;
/**
* Corresponds to a filter object in the web app. Holds one instance only.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
30 public class FilterConfiguration implements javax.servlet.FilterConfig {
31 final String ELEM_NAME = "filter-name";
32 final String ELEM_DISPLAY_NAME = "display-name";
33 final String ELEM_CLASS = "filter-class";
34 final String ELEM_DESCRIPTION = "description";
35 final String ELEM_INIT_PARAM = "init-param";
36 final String ELEM_INIT_PARAM_NAME = "param-name";
37 final String ELEM_INIT_PARAM_VALUE = "param-value";
39 private String filterName;
40 private String classFile;
41 private Filter instance;
42 private Map initParameters;
43 private ServletContext context;
44 private ClassLoader loader;
private boolean unavailableException;
46 private Object filterSemaphore = new Boolean( true );
48 protected FilterConfiguration( ServletContext context, ClassLoader loader ) {
this.context = context;
this.loader = loader;
this.initParameters = new Hashtable( );
}
/**
* Constructor
*/
57 public FilterConfiguration( ServletContext context, ClassLoader loader, Node elm ) {
this( context, loader );
// Parse the web.xml file entry
for ( int n = 0; n < elm.getChildNodes( ).getLength( ); n++ ) {
Node child = elm.getChildNodes( ).item( n );
if ( child.getNodeType( ) != Node.ELEMENT_NODE )
continue;
String nodeName = child.getNodeName( );
// Construct the servlet instances
if ( nodeName.equals( ELEM_NAME ) )
this.filterName = WebAppConfiguration.getTextFromNode( child );
else if ( nodeName.equals( ELEM_CLASS ) )
this.classFile = WebAppConfiguration.getTextFromNode( child );
else if ( nodeName.equals( ELEM_INIT_PARAM ) ) {
String paramName = null;
String paramValue = null;
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node paramNode = child.getChildNodes( ).item( k );
if ( paramNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( paramNode.getNodeName( ).equals(
ELEM_INIT_PARAM_NAME ) )
paramName = WebAppConfiguration.getTextFromNode( paramNode );
else if ( paramNode.getNodeName( ).equals(
ELEM_INIT_PARAM_VALUE ) )
paramValue = WebAppConfiguration.getTextFromNode( paramNode );
}
if ( ( paramName != null ) && ( paramValue != null ) )
this.initParameters.put( paramName, paramValue );
}
}
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"FilterConfiguration.DeployedInstance", new String[] {
this.filterName, this.classFile } );
}
95 public String getFilterName( ) {
return this.filterName;
}
99 public String getInitParameter( String paramName ) {
return ( String ) this.initParameters.get( paramName );
}
103 public Enumeration getInitParameterNames( ) {
return Collections.enumeration( this.initParameters.keySet( ) );
}
107 public ServletContext getServletContext( ) {
return this.context;
}
/**
* Implements the first-time-init of an instance, and wraps it in a
* dispatcher.
*/
115 public Filter getFilter( ) throws ServletException {
synchronized ( this.filterSemaphore ) {
if ( isUnavailable( ) )
throw new WinstoneException( Launcher.RESOURCES
.getString( "FilterConfiguration.FilterUnavailable" ) );
else if ( this.instance == null )
try {
ClassLoader cl = Thread.currentThread( )
.getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.loader );
Class filterClass = Class.forName( classFile, true,
this.loader );
this.instance = ( Filter ) filterClass.newInstance( );
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"FilterConfiguration.init", this.filterName );
// Initialise with the correct classloader
this.instance.init( this );
Thread.currentThread( ).setContextClassLoader( cl );
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"FilterConfiguration.ClassLoadError",
this.classFile, err );
} catch ( IllegalAccessException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"FilterConfiguration.ClassLoadError",
this.classFile, err );
} catch ( InstantiationException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"FilterConfiguration.ClassLoadError",
this.classFile, err );
} catch ( ServletException err ) {
this.instance = null;
if ( err instanceof UnavailableException )
setUnavailable( );
throw err;
// throw new WinstoneException( resources
// .getString( "FilterConfiguration.InitError" ), err );
}
}
return this.instance;
}
/**
* Called when it's time for the container to shut this servlet down.
*/
162 public void destroy( ) {
synchronized ( this.filterSemaphore ) {
setUnavailable( );
}
}
168 public String toString( ) {
return Launcher.RESOURCES.getString( "FilterConfiguration.Description",
new String[] { this.filterName, this.classFile } );
}
173 public boolean isUnavailable( ) {
return this.unavailableException;
}
177 protected void setUnavailable( ) {
this.unavailableException = true;
if ( this.instance != null ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"FilterConfiguration.destroy", this.filterName );
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.loader );
this.instance.destroy( );
Thread.currentThread( ).setContextClassLoader( cl );
this.instance = null;
}
}
190 public void execute( ServletRequest request, ServletResponse response, FilterChain chain )
throws ServletException, IOException {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.loader );
try {
getFilter( ).doFilter( request, response, chain );
} catch ( UnavailableException err ) {
setUnavailable( );
throw new ServletException( Launcher.RESOURCES
.getString( "RequestDispatcher.FilterError" ), err );
} finally {
Thread.currentThread( ).setContextClassLoader( cl );
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* Manages the references to individual webapps within the container. This object handles
* the mapping of url-prefixes to webapps, and init and shutdown of any webapps it manages.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: HostConfiguration.java, v 1.8 2007/08/02 06:16:00 rickknowles Exp $
*/
35 public class HostConfiguration implements Runnable {
private static final long FLUSH_PERIOD = 60000L;
39 private static final String WEB_INF = "WEB-INF";
40 private static final String WEB_XML = "web.xml";
42 private String hostname;
43 private Map args;
44 private Map webapps;
45 private Cluster cluster;
46 private ObjectPool objectPool;
47 private ClassLoader commonLibCL;
48 private File commonLibCLPaths[];
50 private Thread thread;
52 public HostConfiguration( String hostname, Cluster cluster, ObjectPool objectPool, ClassLoader commonLibCL,
53 File commonLibCLPaths[], Map args, String webappsDirName ) throws IOException {
this.hostname = hostname;
this.args = args;
this.webapps = new Hashtable( );
this.cluster = cluster;
this.objectPool = objectPool;
this.commonLibCL = commonLibCL;
this.commonLibCLPaths = commonLibCLPaths;
// Is this the single or multiple configuration ? Check args
String warfile = ( String ) args.get( "warfile" );
String webroot = ( String ) args.get( "webroot" );
// If single-webapp mode
if ( ( webappsDirName == null ) && ( ( warfile != null ) || ( webroot != null ) ) ) {
String prefix = ( String ) args.get( "prefix" );
if ( prefix == null ) {
prefix = "";
}
try {
this.webapps.put( prefix, initWebApp( prefix, getWebRoot( webroot, warfile ), "webapp" ) );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err );
}
}
// Otherwise multi-webapp mode
else {
initMultiWebappDir( webappsDirName );
}
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostConfig.InitComplete",
new String[] {this.webapps.size( ) + "", this.webapps.keySet( ) + ""} );
this.thread = new Thread( this, "WinstoneHostConfigurationMgmt:" + this.hostname );
this.thread.setDaemon( true );
this.thread.start( );
}
91 public WebAppConfiguration getWebAppByURI( String uri ) {
if ( uri == null ) {
return null;
} else if ( uri.equals( "/" ) || uri.equals( "" ) ) {
return ( WebAppConfiguration ) this.webapps.get( "" );
} else if ( uri.startsWith( "/" ) ) {
String decoded = WinstoneRequest.decodeURLToken( uri );
String noLeadingSlash = decoded.substring( 1 );
int slashPos = noLeadingSlash.indexOf( "/" );
if ( slashPos == -1 ) {
return ( WebAppConfiguration ) this.webapps.get( decoded );
} else {
return ( WebAppConfiguration ) this.webapps.get(
decoded.substring( 0, slashPos + 1 ) );
}
} else {
return null;
}
}
111 protected WebAppConfiguration initWebApp( String prefix, File webRoot,
112 String contextName ) throws IOException {
Node webXMLParentNode = null;
File webInfFolder = new File( webRoot, WEB_INF );
if ( webInfFolder.exists( ) ) {
File webXmlFile = new File( webInfFolder, WEB_XML );
if ( webXmlFile.exists( ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostConfig.ParsingWebXml" );
Document webXMLDoc = new WebXmlParser( this.commonLibCL )
.parseStreamToXML( webXmlFile );
if ( webXMLDoc != null ) {
webXMLParentNode = webXMLDoc.getDocumentElement( );
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostConfig.WebXmlParseComplete" );
} else {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostConfig.WebXmlParseFailed" );
}
}
}
// Instantiate the webAppConfig
return new WebAppConfiguration( this, this.cluster, webRoot
.getCanonicalPath( ), prefix, this.objectPool, this.args,
webXMLParentNode, this.commonLibCL,
this.commonLibCLPaths, contextName );
}
137 public String getHostname( ) {
return this.hostname;
}
/**
* Destroy this webapp instance. Kills the webapps, plus any servlets,
* attributes, etc
*
* @param webApp The webapp to destroy
*/
147 private void destroyWebApp( String prefix ) {
WebAppConfiguration webAppConfig = ( WebAppConfiguration ) this.webapps.get( prefix );
if ( webAppConfig != null ) {
webAppConfig.destroy( );
this.webapps.remove( prefix );
}
}
155 public void destroy( ) {
Set prefixes = new HashSet( this.webapps.keySet( ) );
for ( Iterator i = prefixes.iterator( ); i.hasNext( ); ) {
destroyWebApp( ( String ) i.next( ) );
}
if ( this.thread != null ) {
this.thread.interrupt( );
}
}
165 public void invalidateExpiredSessions( ) {
Set webapps = new HashSet( this.webapps.values( ) );
for ( Iterator i = webapps.iterator( ); i.hasNext( ); ) {
( ( WebAppConfiguration ) i.next( ) ).invalidateExpiredSessions( );
}
}
172 public void run( ) {
boolean interrupted = false;
while ( !interrupted ) {
try {
Thread.sleep( FLUSH_PERIOD );
invalidateExpiredSessions( );
} catch ( InterruptedException err ) {
interrupted = true;
}
}
this.thread = null;
}
185 public void reloadWebApp( String prefix ) throws IOException {
WebAppConfiguration webAppConfig = ( WebAppConfiguration ) this.webapps.get( prefix );
if ( webAppConfig != null ) {
String webRoot = webAppConfig.getWebroot( );
String contextName = webAppConfig.getContextName( );
destroyWebApp( prefix );
try {
this.webapps.put( prefix, initWebApp( prefix, new File( webRoot ), contextName ) );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err );
}
} else {
throw new WinstoneException( Launcher.RESOURCES.getString( "HostConfig.PrefixUnknown", prefix ) );
}
}
/**
* Setup the webroot. If a warfile is supplied, extract any files that the
* war file is newer than. If none is supplied, use the default temp
* directory.
*/
206 protected File getWebRoot( String requestedWebroot, String warfileName ) throws IOException {
if ( warfileName != null ) {
Logger.log( Logger.INFO, Launcher.RESOURCES,
"HostConfig.BeginningWarExtraction" );
// open the war file
File warfileRef = new File( warfileName );
if ( !warfileRef.exists( ) || !warfileRef.isFile( ) )
throw new WinstoneException( Launcher.RESOURCES.getString(
"HostConfig.WarFileInvalid", warfileName ) );
// Get the webroot folder ( or a temp dir if none supplied )
File unzippedDir = null;
if ( requestedWebroot != null ) {
unzippedDir = new File( requestedWebroot );
} else {
File tempFile = File.createTempFile( "dummy", "dummy" );
String userName = System.getProperty( "user.name" );
unzippedDir = new File( tempFile.getParent( ), "winstone/" +
( userName != null ? WinstoneResourceBundle.globalReplace( userName,
new String[][] {{"/", ""}, {"\\", ""}, {", ", ""}} ) + "/" : "" ) +
warfileRef.getName( ) );
tempFile.delete( );
}
if ( unzippedDir.exists( ) ) {
if ( !unzippedDir.isDirectory( ) ) {
throw new WinstoneException( Launcher.RESOURCES.getString(
"HostConfig.WebRootNotDirectory", unzippedDir.getPath( ) ) );
} else {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"HostConfig.WebRootExists", unzippedDir.getCanonicalPath( ) );
}
} else {
unzippedDir.mkdirs( );
}
// Iterate through the files
JarFile warArchive = new JarFile( warfileRef );
for ( Enumeration e = warArchive.entries( ); e.hasMoreElements( ); ) {
JarEntry element = ( JarEntry ) e.nextElement( );
if ( element.isDirectory( ) ) {
continue;
}
String elemName = element.getName( );
// If archive date is newer than unzipped file, overwrite
File outFile = new File( unzippedDir, elemName );
if ( outFile.exists( ) && ( outFile.lastModified( ) > warfileRef.lastModified( ) ) ) {
continue;
}
outFile.getParentFile( ).mkdirs( );
byte buffer[] = new byte[8192];
// Copy out the extracted file
InputStream inContent = warArchive.getInputStream( element );
OutputStream outStream = new FileOutputStream( outFile );
int readBytes = inContent.read( buffer );
while ( readBytes != -1 ) {
outStream.write( buffer, 0, readBytes );
readBytes = inContent.read( buffer );
}
inContent.close( );
outStream.close( );
}
// Return webroot
return unzippedDir;
} else {
return new File( requestedWebroot );
}
}
278 protected void initMultiWebappDir( String webappsDirName ) throws IOException {
if ( webappsDirName == null ) {
webappsDirName = "webapps";
}
File webappsDir = new File( webappsDirName );
if ( !webappsDir.exists( ) ) {
throw new WinstoneException( Launcher.RESOURCES.getString( "HostConfig.WebAppDirNotFound", webappsDirName ) );
} else if ( !webappsDir.isDirectory( ) ) {
throw new WinstoneException( Launcher.RESOURCES.getString( "HostConfig.WebAppDirIsNotDirectory", webappsDirName ) );
} else {
File children[] = webappsDir.listFiles( );
for ( int n = 0; n < children.length; n++ ) {
String childName = children[n].getName( );
// Check any directories for warfiles that match, and skip: only deploy the war file
if ( children[n].isDirectory( ) ) {
File matchingWarFile = new File( webappsDir, children[n].getName( ) + ".war" );
if ( matchingWarFile.exists( ) && matchingWarFile.isFile( ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostConfig.SkippingWarfileDir", childName );
} else {
String prefix = childName.equalsIgnoreCase( "ROOT" ) ? "" : "/" + childName;
if ( !this.webapps.containsKey( prefix ) ) {
try {
WebAppConfiguration webAppConfig = initWebApp( prefix, children[n], childName );
this.webapps.put( webAppConfig.getContextPath( ), webAppConfig );
Logger.log( Logger.INFO, Launcher.RESOURCES, "HostConfig.DeployingWebapp", childName );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err );
}
}
}
} else if ( childName.endsWith( ".war" ) ) {
String outputName = childName.substring( 0, childName.lastIndexOf( ".war" ) );
String prefix = outputName.equalsIgnoreCase( "ROOT" ) ? "" : "/" + outputName;
if ( !this.webapps.containsKey( prefix ) ) {
File outputDir = new File( webappsDir, outputName );
outputDir.mkdirs( );
try {
WebAppConfiguration webAppConfig = initWebApp( prefix,
getWebRoot( new File( webappsDir, outputName ).getCanonicalPath( ),
children[n].getCanonicalPath( ) ), outputName );
this.webapps.put( webAppConfig.getContextPath( ), webAppConfig );
Logger.log( Logger.INFO, Launcher.RESOURCES, "HostConfig.DeployingWebapp", childName );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err );
}
}
}
}
}
}
331 public WebAppConfiguration getWebAppBySessionKey( String sessionKey ) {
List allwebapps = new ArrayList( this.webapps.values( ) );
for ( Iterator i = allwebapps.iterator( ); i.hasNext( ); ) {
WebAppConfiguration webapp = ( WebAppConfiguration ) i.next( );
WinstoneSession session = webapp.getSessionById( sessionKey, false );
if ( session != null ) {
return webapp;
}
}
return null;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Manages the references to individual hosts within the container. This object handles
* the mapping of ip addresses and hostnames to groups of webapps, and init and
* shutdown of any hosts it manages.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: HostGroup.java, v 1.4 2006/03/24 17:24:21 rickknowles Exp $
*/
25 public class HostGroup {
27 private final static String DEFAULT_HOSTNAME = "default";
// private Map args;
30 private Map hostConfigs;
31 private String defaultHostName;
33 public HostGroup( Cluster cluster,
34 ObjectPool objectPool, ClassLoader commonLibCL,
35 File commonLibCLPaths[], Map args ) throws IOException {
// this.args = args;
this.hostConfigs = new Hashtable( );
// Is this the single or multiple configuration ? Check args
String hostDirName = ( String ) args.get( "hostsDir" );
String webappsDirName = ( String ) args.get( "webappsDir" );
// If host mode
if ( hostDirName == null ) {
initHost( webappsDirName, DEFAULT_HOSTNAME, cluster, objectPool, commonLibCL,
commonLibCLPaths, args );
this.defaultHostName = DEFAULT_HOSTNAME;
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostGroup.InitSingleComplete",
new String[] {this.hostConfigs.size( ) + "", this.hostConfigs.keySet( ) + ""} );
}
// Otherwise multi-webapp mode
else {
initMultiHostDir( hostDirName, cluster, objectPool, commonLibCL,
commonLibCLPaths, args );
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostGroup.InitMultiComplete",
new String[] {this.hostConfigs.size( ) + "", this.hostConfigs.keySet( ) + ""} );
}
}
60 public HostConfiguration getHostByName( String hostname ) {
if ( ( hostname != null ) && ( this.hostConfigs.size( ) > 1 ) ) {
HostConfiguration host = ( HostConfiguration ) this.hostConfigs.get( hostname );
if ( host != null ) {
return host;
}
}
return ( HostConfiguration ) this.hostConfigs.get( this.defaultHostName );
}
70 public void destroy( ) {
Set hostnames = new HashSet( this.hostConfigs.keySet( ) );
for ( Iterator i = hostnames.iterator( ); i.hasNext( ); ) {
String hostname = ( String ) i.next( );
HostConfiguration host = ( HostConfiguration ) this.hostConfigs.get( hostname );
host.destroy( );
this.hostConfigs.remove( hostname );
}
this.hostConfigs.clear( );
}
81 protected void initHost( String webappsDirName, String hostname, Cluster cluster,
82 ObjectPool objectPool, ClassLoader commonLibCL,
83 File commonLibCLPaths[], Map args ) throws IOException {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "HostGroup.DeployingHost", hostname );
HostConfiguration config = new HostConfiguration( hostname, cluster, objectPool, commonLibCL,
commonLibCLPaths, args, webappsDirName );
this.hostConfigs.put( hostname, config );
}
90 protected void initMultiHostDir( String hostsDirName, Cluster cluster,
91 ObjectPool objectPool, ClassLoader commonLibCL,
92 File commonLibCLPaths[], Map args ) throws IOException {
if ( hostsDirName == null ) {
hostsDirName = "hosts";
}
File hostsDir = new File( hostsDirName );
if ( !hostsDir.exists( ) ) {
throw new WinstoneException( Launcher.RESOURCES.getString( "HostGroup.HostsDirNotFound", hostsDirName ) );
} else if ( !hostsDir.isDirectory( ) ) {
throw new WinstoneException( Launcher.RESOURCES.getString( "HostGroup.HostsDirIsNotDirectory", hostsDirName ) );
} else {
File children[] = hostsDir.listFiles( );
if ( ( children == null ) || ( children.length == 0 ) ) {
throw new WinstoneException( Launcher.RESOURCES.getString( "HostGroup.HostsDirIsEmpty", hostsDirName ) );
}
for ( int n = 0; n < children.length; n++ ) {
String childName = children[n].getName( );
// Mount directories as host dirs
if ( children[n].isDirectory( ) ) {
if ( !this.hostConfigs.containsKey( childName ) ) {
initHost( children[n].getCanonicalPath( ), childName, cluster,
objectPool, commonLibCL, commonLibCLPaths, args );
}
}
if ( ( defaultHostName == null ) || childName.equals( DEFAULT_HOSTNAME ) ) {
this.defaultHostName = childName;
}
}
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Implements the main listener daemon thread. This is the class that gets
* launched by the command line, and owns the server socket, etc. Note that this
* class is also used as the base class for the HTTPS listener.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: HttpListener.java, v 1.15 2007/05/01 04:39:49 rickknowles Exp $
*/
29 public class HttpListener implements Listener, Runnable {
protected static int LISTENER_TIMEOUT = 5000; // every 5s reset the
// listener socket
protected static int CONNECTION_TIMEOUT = 60000;
protected static int BACKLOG_COUNT = 5000;
protected static boolean DEFAULT_HNL = false;
protected static int KEEP_ALIVE_TIMEOUT = 10000;
protected static int KEEP_ALIVE_SLEEP = 20;
protected static int KEEP_ALIVE_SLEEP_MAX = 500;
38 protected HostGroup hostGroup;
39 protected ObjectPool objectPool;
protected boolean doHostnameLookups;
protected int listenPort;
42 protected String listenAddress;
protected boolean interrupted;
45 protected HttpListener( ) {
}
/**
* Constructor
*/
51 public HttpListener( Map args, ObjectPool objectPool, HostGroup hostGroup ) throws IOException {
// Load resources
this.hostGroup = hostGroup;
this.objectPool = objectPool;
this.listenPort = Integer.parseInt( WebAppConfiguration.stringArg( args,
getConnectorName( ) + "Port", "" + getDefaultPort( ) ) );
this.listenAddress = WebAppConfiguration.stringArg( args,
getConnectorName( ) + "ListenAddress", null );
this.doHostnameLookups = WebAppConfiguration.booleanArg( args,
getConnectorName( ) + "DoHostnameLookups", DEFAULT_HNL );
}
63 public boolean start( ) {
if ( this.listenPort < 0 ) {
return false;
} else {
this.interrupted = false;
Thread thread = new Thread( this, Launcher.RESOURCES.getString(
"Listener.ThreadName", new String[] { getConnectorName( ),
"" + this.listenPort } ) );
thread.setDaemon( true );
thread.start( );
return true;
}
}
/**
* The default port to use - this is just so that we can override for the
* SSL connector.
*/
81 protected int getDefaultPort( ) {
return 8080;
}
/**
* The name to use when getting properties - this is just so that we can
* override for the SSL connector.
*/
89 protected String getConnectorName( ) {
return getConnectorScheme( );
}
93 protected String getConnectorScheme( ) {
return "http";
}
/**
* Gets a server socket - this is mostly for the purpose of allowing an
* override in the SSL connector.
*/
101 protected ServerSocket getServerSocket( ) throws IOException {
ServerSocket ss = this.listenAddress == null ? new ServerSocket(
this.listenPort, BACKLOG_COUNT ) : new ServerSocket(
this.listenPort, BACKLOG_COUNT, InetAddress
.getByName( this.listenAddress ) );
return ss;
}
/**
* The main run method. This continually listens for incoming connections,
* and allocates any that it finds to a request handler thread, before going
* back to listen again.
*/
114 public void run( ) {
try {
ServerSocket ss = getServerSocket( );
ss.setSoTimeout( LISTENER_TIMEOUT );
Logger.log( Logger.INFO, Launcher.RESOURCES, "HttpListener.StartupOK",
new String[] { getConnectorName( ).toUpperCase( ),
this.listenPort + "" } );
// Enter the main loop
while ( !interrupted ) {
// Get the listener
Socket s = null;
try {
s = ss.accept( );
} catch ( java.io.InterruptedIOException err ) {
s = null;
}
// if we actually got a socket, process it. Otherwise go around
// again
if ( s != null )
this.objectPool.handleRequest( s, this );
}
// Close server socket
ss.close( );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "HttpListener.ShutdownError",
getConnectorName( ).toUpperCase( ), err );
}
Logger.log( Logger.INFO, Launcher.RESOURCES, "HttpListener.ShutdownOK",
getConnectorName( ).toUpperCase( ) );
}
/**
* Interrupts the listener thread. This will trigger a listener shutdown
* once the so timeout has passed.
*/
153 public void destroy( ) {
this.interrupted = true;
}
/**
* Called by the request handler thread, because it needs specific setup
* code for this connection's protocol ( ie construction of request/response
* objects, in/out streams, etc ).
*
* This implementation parses incoming AJP13 packets, and builds an
* outputstream that is capable of writing back the response in AJP13
* packets.
*/
166 public void allocateRequestResponse( Socket socket, InputStream inSocket,
167 OutputStream outSocket, RequestHandlerThread handler,
boolean iAmFirst ) throws SocketException, IOException {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"HttpListener.AllocatingRequest", Thread.currentThread( )
.getName( ) );
socket.setSoTimeout( CONNECTION_TIMEOUT );
// Build input/output streams, plus request/response
WinstoneInputStream inData = new WinstoneInputStream( inSocket );
WinstoneOutputStream outData = new WinstoneOutputStream( outSocket, false );
WinstoneRequest req = this.objectPool.getRequestFromPool( );
WinstoneResponse rsp = this.objectPool.getResponseFromPool( );
outData.setResponse( rsp );
req.setInputStream( inData );
rsp.setOutputStream( outData );
rsp.setRequest( req );
// rsp.updateContentTypeHeader( "text/html" );
req.setHostGroup( this.hostGroup );
// Set the handler's member variables so it can execute the servlet
handler.setRequest( req );
handler.setResponse( rsp );
handler.setInStream( inData );
handler.setOutStream( outData );
// If using this listener, we must set the server header now, because it
// must be the first header. Ajp13 listener can defer to the Apache Server
// header
rsp.setHeader( "Server", Launcher.RESOURCES.getString( "ServerVersion" ) );
}
/**
* Called by the request handler thread, because it needs specific shutdown
* code for this connection's protocol ( ie releasing input/output streams,
* etc ).
*/
203 public void deallocateRequestResponse( RequestHandlerThread handler,
204 WinstoneRequest req, WinstoneResponse rsp,
205 WinstoneInputStream inData, WinstoneOutputStream outData )
throws IOException {
handler.setInStream( null );
handler.setOutStream( null );
handler.setRequest( null );
handler.setResponse( null );
if ( req != null )
this.objectPool.releaseRequestToPool( req );
if ( rsp != null )
this.objectPool.releaseResponseToPool( rsp );
}
217 public String parseURI( RequestHandlerThread handler, WinstoneRequest req,
218 WinstoneResponse rsp, WinstoneInputStream inData, Socket socket,
boolean iAmFirst ) throws IOException {
parseSocketInfo( socket, req );
// Read the header line ( because this is the first line of the request,
// apply keep-alive timeouts to it if we are not the first request )
if ( !iAmFirst ) {
socket.setSoTimeout( KEEP_ALIVE_TIMEOUT );
}
byte uriBuffer[] = null;
try {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "HttpListener.WaitingForURILine" );
uriBuffer = inData.readLine( );
} catch ( InterruptedIOException err ) {
// keep alive timeout ? ignore if not first
if ( iAmFirst ) {
throw err;
} else {
return null;
}
} finally {
try {socket.setSoTimeout( CONNECTION_TIMEOUT );} catch ( Throwable err ) {}
}
handler.setRequestStartTime( );
// Get header data ( eg protocol, method, uri, headers, etc )
String uriLine = new String( uriBuffer );
if ( uriLine.trim( ).equals( "" ) )
throw new SocketException( "Empty URI Line" );
String servletURI = parseURILine( uriLine, req, rsp );
parseHeaders( req, inData );
rsp.extractRequestKeepAliveHeader( req );
int contentLength = req.getContentLength( );
if ( contentLength != -1 )
inData.setContentLength( contentLength );
return servletURI;
}
/**
* Called by the request handler thread, because it needs specific shutdown
* code for this connection's protocol if the keep-alive period expires ( ie
* closing sockets, etc ).
*
* This implementation simply shuts down the socket and streams.
*/
264 public void releaseSocket( Socket socket, InputStream inSocket,
265 OutputStream outSocket ) throws IOException {
// Logger.log( Logger.FULL_DEBUG, "Releasing socket: " +
// Thread.currentThread( ).getName( ) );
inSocket.close( );
outSocket.close( );
socket.close( );
}
273 protected void parseSocketInfo( Socket socket, WinstoneRequest req )
throws IOException {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "HttpListener.ParsingSocketInfo" );
req.setScheme( getConnectorScheme( ) );
req.setServerPort( socket.getLocalPort( ) );
req.setLocalPort( socket.getLocalPort( ) );
req.setLocalAddr( socket.getLocalAddress( ).getHostAddress( ) );
req.setRemoteIP( socket.getInetAddress( ).getHostAddress( ) );
req.setRemotePort( socket.getPort( ) );
if ( this.doHostnameLookups ) {
req.setServerName( socket.getLocalAddress( ).getHostName( ) );
req.setRemoteName( socket.getInetAddress( ).getHostName( ) );
req.setLocalName( socket.getLocalAddress( ).getHostName( ) );
} else {
req.setServerName( socket.getLocalAddress( ).getHostAddress( ) );
req.setRemoteName( socket.getInetAddress( ).getHostAddress( ) );
req.setLocalName( socket.getLocalAddress( ).getHostAddress( ) );
}
}
/**
* Tries to wait for extra requests on the same socket. If any are found
* before the timeout expires, it exits with a true, indicating a new
* request is waiting. If the protocol does not support keep-alives, or the
* request instructed us to close the connection, or the timeout expires,
* return a false, instructing the handler thread to begin shutting down the
* socket and relase itself.
*/
301 public boolean processKeepAlive( WinstoneRequest request,
302 WinstoneResponse response, InputStream inSocket )
throws IOException, InterruptedException {
// Try keep alive if allowed
boolean continueFlag = !response.closeAfterRequest( );
return continueFlag;
}
/**
* Processes the uri line into it's component parts, determining protocol,
* method and uri
*/
313 private String parseURILine( String uriLine, WinstoneRequest req,
314 WinstoneResponse rsp ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "HttpListener.UriLine", uriLine.trim( ) );
// Method
int spacePos = uriLine.indexOf( ' ' );
if ( spacePos == -1 )
throw new WinstoneException( Launcher.RESOURCES.getString(
"HttpListener.ErrorUriLine", uriLine ) );
String method = uriLine.substring( 0, spacePos ).toUpperCase( );
String fullURI = null;
// URI
String remainder = uriLine.substring( spacePos + 1 );
spacePos = remainder.indexOf( ' ' );
if ( spacePos == -1 ) {
fullURI = trimHostName( remainder.trim( ) );
req.setProtocol( "HTTP/0.9" );
rsp.setProtocol( "HTTP/0.9" );
} else {
fullURI = trimHostName( remainder.substring( 0, spacePos ).trim( ) );
String protocol = remainder.substring( spacePos + 1 ).trim( ).toUpperCase( );
req.setProtocol( protocol );
rsp.setProtocol( protocol );
}
req.setMethod( method );
// req.setRequestURI( fullURI );
return fullURI;
}
344 private String trimHostName( String input ) {
if ( input == null )
return null;
else if ( input.startsWith( "/" ) )
return input;
int hostStart = input.indexOf( "://" );
if ( hostStart == -1 )
return input;
String hostName = input.substring( hostStart + 3 );
int pathStart = hostName.indexOf( '/' );
if ( pathStart == -1 )
return "/";
else
return hostName.substring( pathStart );
}
/**
* Parse the incoming stream into a list of headers ( stopping at the first
* blank line ), then call the parseHeaders( req, list ) method on that list.
*/
365 public void parseHeaders( WinstoneRequest req, WinstoneInputStream inData )
throws IOException {
List headerList = new ArrayList( );
if ( !req.getProtocol( ).startsWith( "HTTP/0" ) ) {
// Loop to get headers
byte headerBuffer[] = inData.readLine( );
String headerLine = new String( headerBuffer );
while ( headerLine.trim( ).length( ) > 0 ) {
if ( headerLine.indexOf( ':' ) != -1 ) {
headerList.add( headerLine.trim( ) );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"HttpListener.Header", headerLine.trim( ) );
}
headerBuffer = inData.readLine( );
headerLine = new String( headerBuffer );
}
}
// If no headers available, parse an empty list
req.parseHeaders( headerList );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
/**
* Handles setup and teardown of the JNDI context
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: JNDIManager.java, v 1.2 2006/02/28 07:32:47 rickknowles Exp $
*/
public interface JNDIManager {
/**
* Add the objects passed to the constructor to the JNDI Context addresses
* specified
*/
20 public void setup( );
/**
* Remove the objects under administration from the JNDI Context, and then
* destroy the objects
*/
26 public void tearDown( );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.ServerSocket;
import java.net.Socket;
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 java.util.Properties;
/**
* Implements the main launcher daemon thread. This is the class that gets
* launched by the command line, and owns the server socket, etc.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Launcher.java, v 1.29 2007/04/23 02:55:35 rickknowles Exp $
*/
36 public class Launcher implements Runnable {
38 static final String HTTP_LISTENER_CLASS = "winstone.HttpListener";
39 static final String HTTPS_LISTENER_CLASS = "winstone.ssl.HttpsListener";
40 static final String AJP_LISTENER_CLASS = "winstone.ajp13.Ajp13Listener";
41 static final String CLUSTER_CLASS = "winstone.cluster.SimpleCluster";
42 static final String DEFAULT_JNDI_MGR_CLASS = "winstone.jndi.ContainerJNDIManager";
public static final byte SHUTDOWN_TYPE = ( byte ) '0';
public static final byte RELOAD_TYPE = ( byte ) '4';
private int CONTROL_TIMEOUT = 2000; // wait 2s for control connection
private int DEFAULT_CONTROL_PORT = -1;
50 private Thread controlThread;
51 public final static WinstoneResourceBundle RESOURCES = new WinstoneResourceBundle( "winstone.LocalStrings" );
private int controlPort;
53 private HostGroup hostGroup;
54 private ObjectPool objectPool;
55 private List listeners;
56 private Map args;
57 private Cluster cluster;
58 private JNDIManager globalJndiManager;
/**
* Constructor - initialises the web app, object pools, control port and the
* available protocol listeners.
*/
64 public Launcher( Map args ) throws IOException {
boolean useJNDI = WebAppConfiguration.booleanArg( args, "useJNDI", false );
// Set jndi resource handler if not set ( workaround for JamVM bug )
if ( useJNDI ) try {
Class ctxFactoryClass = Class.forName( "winstone.jndi.java.javaURLContextFactory" );
if ( System.getProperty( "java.naming.factory.initial" ) == null ) {
System.setProperty( "java.naming.factory.initial", ctxFactoryClass.getName( ) );
}
if ( System.getProperty( "java.naming.factory.url.pkgs" ) == null ) {
System.setProperty( "java.naming.factory.url.pkgs", "winstone.jndi" );
}
} catch ( ClassNotFoundException err ) {}
Logger.log( Logger.MAX, RESOURCES, "Launcher.StartupArgs", args + "" );
this.args = args;
this.controlPort = ( args.get( "controlPort" ) == null ? DEFAULT_CONTROL_PORT
: Integer.parseInt( ( String ) args.get( "controlPort" ) ) );
// Check for java home
List jars = new ArrayList( );
List commonLibCLPaths = new ArrayList( );
String defaultJavaHome = System.getProperty( "java.home" );
String javaHome = WebAppConfiguration.stringArg( args, "javaHome", defaultJavaHome );
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.UsingJavaHome", javaHome );
String toolsJarLocation = WebAppConfiguration.stringArg( args, "toolsJar", null );
File toolsJar = null;
if ( toolsJarLocation == null ) {
toolsJar = new File( javaHome, "lib/tools.jar" );
// first try - if it doesn't exist, try up one dir since we might have
// the JRE home by mistake
if ( !toolsJar.exists( ) ) {
File javaHome2 = new File( javaHome ).getParentFile( );
File toolsJar2 = new File( javaHome2, "lib/tools.jar" );
if ( toolsJar2.exists( ) ) {
javaHome = javaHome2.getCanonicalPath( );
toolsJar = toolsJar2;
}
}
} else {
toolsJar = new File( toolsJarLocation );
}
// Add tools jar to classloader path
if ( toolsJar.exists( ) ) {
jars.add( toolsJar.toURL( ) );
commonLibCLPaths.add( toolsJar );
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.AddedCommonLibJar",
toolsJar.getName( ) );
} else if ( WebAppConfiguration.booleanArg( args, "useJasper", false ) )
Logger.log( Logger.WARNING, RESOURCES, "Launcher.ToolsJarNotFound" );
// Set up common lib class loader
String commonLibCLFolder = WebAppConfiguration.stringArg( args,
"commonLibFolder", "lib" );
File libFolder = new File( commonLibCLFolder );
if ( libFolder.exists( ) && libFolder.isDirectory( ) ) {
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.UsingCommonLib",
libFolder.getCanonicalPath( ) );
File children[] = libFolder.listFiles( );
for ( int n = 0; n < children.length; n++ )
if ( children[n].getName( ).endsWith( ".jar" )
|| children[n].getName( ).endsWith( ".zip" ) ) {
jars.add( children[n].toURL( ) );
commonLibCLPaths.add( children[n] );
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.AddedCommonLibJar",
children[n].getName( ) );
}
} else {
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.NoCommonLib" );
}
ClassLoader commonLibCL = new URLClassLoader( ( URL[] ) jars.toArray( new URL[jars.size( )] ),
getClass( ).getClassLoader( ) );
Logger.log( Logger.MAX, RESOURCES, "Launcher.CLClassLoader",
commonLibCL.toString( ) );
Logger.log( Logger.MAX, RESOURCES, "Launcher.CLClassLoader",
commonLibCLPaths.toString( ) );
this.objectPool = new ObjectPool( args );
// Optionally set up clustering if enabled and libraries are available
String useCluster = ( String ) args.get( "useCluster" );
boolean switchOnCluster = ( useCluster != null )
&& ( useCluster.equalsIgnoreCase( "true" ) || useCluster
.equalsIgnoreCase( "yes" ) );
if ( switchOnCluster ) {
if ( this.controlPort < 0 ) {
Logger.log( Logger.INFO, RESOURCES,
"Launcher.ClusterOffNoControlPort" );
} else {
String clusterClassName = WebAppConfiguration.stringArg( args, "clusterClassName",
CLUSTER_CLASS ).trim( );
try {
Class clusterClass = Class.forName( clusterClassName );
Constructor clusterConstructor = clusterClass
.getConstructor( new Class[] { Map.class, Integer.class } );
this.cluster = ( Cluster ) clusterConstructor
.newInstance( new Object[] { args, new Integer( this.controlPort ) } );
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.ClusterNotFound" );
} catch ( Throwable err ) {
Logger.log( Logger.WARNING, RESOURCES, "Launcher.ClusterStartupError", err );
}
}
}
// If jndi is enabled, run the container wide jndi populator
if ( useJNDI ) {
String jndiMgrClassName = WebAppConfiguration.stringArg( args, "containerJndiClassName",
DEFAULT_JNDI_MGR_CLASS ).trim( );
try {
// Build the realm
Class jndiMgrClass = Class.forName( jndiMgrClassName, true, commonLibCL );
Constructor jndiMgrConstr = jndiMgrClass.getConstructor( new Class[] {
Map.class, List.class, ClassLoader.class } );
this.globalJndiManager = ( JNDIManager ) jndiMgrConstr.newInstance( new Object[] {
args, null, commonLibCL } );
this.globalJndiManager.setup( );
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.DEBUG, RESOURCES,
"Launcher.JNDIDisabled" );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, RESOURCES,
"Launcher.JNDIError", jndiMgrClassName, err );
}
}
// Open the web apps
this.hostGroup = new HostGroup( this.cluster, this.objectPool, commonLibCL,
( File [] ) commonLibCLPaths.toArray( new File[0] ), args );
// Create connectors ( http, https and ajp )
this.listeners = new ArrayList( );
spawnListener( HTTP_LISTENER_CLASS );
spawnListener( AJP_LISTENER_CLASS );
try {
Class.forName( "javax.net.ServerSocketFactory" );
spawnListener( HTTPS_LISTENER_CLASS );
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.DEBUG, RESOURCES,
"Launcher.NeedsJDK14", HTTPS_LISTENER_CLASS );
}
this.controlThread = new Thread( this, RESOURCES.getString(
"Launcher.ThreadName", "" + this.controlPort ) );
this.controlThread.setDaemon( false );
this.controlThread.start( );
Runtime.getRuntime( ).addShutdownHook( new ShutdownHook( this ) );
}
/**
* Instantiates listeners. Note that an exception thrown in the
* constructor is interpreted as the listener being disabled, so
* don't do anything too adventurous in the constructor, or if you do,
* catch and log any errors locally before rethrowing.
*/
226 protected void spawnListener( String listenerClassName ) {
try {
Class listenerClass = Class.forName( listenerClassName );
Constructor listenerConstructor = listenerClass
.getConstructor( new Class[] { Map.class,
ObjectPool.class, HostGroup.class} );
Listener listener = ( Listener ) listenerConstructor
.newInstance( new Object[] { args, this.objectPool,
this.hostGroup } );
if ( listener.start( ) ) {
this.listeners.add( listener );
}
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.INFO, RESOURCES,
"Launcher.ListenerNotFound", listenerClassName );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, RESOURCES,
"Launcher.ListenerStartupError", listenerClassName, err );
}
}
/**
* The main run method. This handles the normal thread processing.
*/
250 public void run( ) {
boolean interrupted = false;
try {
ServerSocket controlSocket = null;
if ( this.controlPort > 0 ) {
controlSocket = new ServerSocket( this.controlPort );
controlSocket.setSoTimeout( CONTROL_TIMEOUT );
}
Logger.log( Logger.INFO, RESOURCES, "Launcher.StartupOK",
new String[] {RESOURCES.getString( "ServerVersion" ),
( this.controlPort > 0 ? "" + this.controlPort
: RESOURCES.getString( "Launcher.ControlDisabled" ) )} );
// Enter the main loop
while ( !interrupted ) {
// this.objectPool.removeUnusedRequestHandlers( );
// this.hostGroup.invalidateExpiredSessions( );
// Check for control request
Socket accepted = null;
try {
if ( controlSocket != null ) {
accepted = controlSocket.accept( );
if ( accepted != null ) {
handleControlRequest( accepted );
}
} else {
Thread.sleep( CONTROL_TIMEOUT );
}
} catch ( InterruptedIOException err ) {
} catch ( InterruptedException err ) {
interrupted = true;
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, RESOURCES,
"Launcher.ShutdownError", err );
} finally {
if ( accepted != null ) {
try {accepted.close( );} catch ( IOException err ) {}
}
if ( Thread.interrupted( ) ) {
interrupted = true;
}
}
}
// Close server socket
if ( controlSocket != null ) {
controlSocket.close( );
}
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, RESOURCES, "Launcher.ShutdownError", err );
}
Logger.log( Logger.INFO, RESOURCES, "Launcher.ControlThreadShutdownOK" );
}
307 protected void handleControlRequest( Socket csAccepted ) throws IOException {
InputStream inSocket = null;
OutputStream outSocket = null;
ObjectInputStream inControl = null;
try {
inSocket = csAccepted.getInputStream( );
int reqType = inSocket.read( );
if ( ( byte ) reqType == SHUTDOWN_TYPE ) {
Logger.log( Logger.INFO, RESOURCES,
"Launcher.ShutdownRequestReceived" );
shutdown( );
} else if ( ( byte ) reqType == RELOAD_TYPE ) {
inControl = new ObjectInputStream( inSocket );
String host = inControl.readUTF( );
String prefix = inControl.readUTF( );
Logger.log( Logger.INFO, RESOURCES, "Launcher.ReloadRequestReceived", host + prefix );
HostConfiguration hostConfig = this.hostGroup.getHostByName( host );
hostConfig.reloadWebApp( prefix );
} else if ( this.cluster != null ) {
outSocket = csAccepted.getOutputStream( );
this.cluster.clusterRequest( ( byte ) reqType,
inSocket, outSocket, csAccepted,
this.hostGroup );
}
} finally {
if ( inControl != null ) {
try {inControl.close( );} catch ( IOException err ) {}
}
if ( inSocket != null ) {
try {inSocket.close( );} catch ( IOException err ) {}
}
if ( outSocket != null ) {
try {outSocket.close( );} catch ( IOException err ) {}
}
}
}
344 public void shutdown( ) {
// Release all listeners/pools/webapps
for ( Iterator i = this.listeners.iterator( ); i.hasNext( ); )
( ( Listener ) i.next( ) ).destroy( );
this.objectPool.destroy( );
if ( this.cluster != null )
this.cluster.destroy( );
this.hostGroup.destroy( );
if ( this.globalJndiManager != null ) {
this.globalJndiManager.tearDown( );
}
if ( this.controlThread != null ) {
this.controlThread.interrupt( );
}
Thread.yield( );
Logger.log( Logger.INFO, RESOURCES, "Launcher.ShutdownOK" );
}
364 public boolean isRunning( ) {
return ( this.controlThread != null ) && this.controlThread.isAlive( );
}
/**
* Main method. This basically just accepts a few args, then initialises the
* listener thread. For now, just shut it down with a control-C.
*/
372 public static void main( String argv[] ) throws IOException {
Map args = getArgsFromCommandLine( argv );
if ( args.containsKey( "usage" ) || args.containsKey( "help" ) ) {
printUsage( );
return;
}
// Check for embedded war
deployEmbeddedWarfile( args );
// Check for embedded warfile
if ( !args.containsKey( "webroot" ) && !args.containsKey( "warfile" )
&& !args.containsKey( "webappsDir" )&& !args.containsKey( "hostsDir" ) ) {
printUsage( );
return;
}
// Launch
try {
new Launcher( args );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, RESOURCES, "Launcher.ContainerStartupError", err );
}
}
397 public static Map getArgsFromCommandLine( String argv[] ) throws IOException {
Map args = loadArgsFromCommandLineAndConfig( argv, "nonSwitch" );
// Small hack to allow re-use of the command line parsing inside the control tool
String firstNonSwitchArgument = ( String ) args.get( "nonSwitch" );
args.remove( "nonSwitch" );
// Check if the non-switch arg is a file or folder, and overwrite the config
if ( firstNonSwitchArgument != null ) {
File webapp = new File( firstNonSwitchArgument );
if ( webapp.exists( ) ) {
if ( webapp.isDirectory( ) ) {
args.put( "webroot", firstNonSwitchArgument );
} else if ( webapp.isFile( ) ) {
args.put( "warfile", firstNonSwitchArgument );
}
}
}
return args;
}
418 public static Map loadArgsFromCommandLineAndConfig( String argv[], String nonSwitchArgName )
throws IOException {
Map args = new HashMap( );
// Load embedded properties file
String embeddedPropertiesFilename = RESOURCES.getString(
"Launcher.EmbeddedPropertiesFile" );
InputStream embeddedPropsStream = Launcher.class.getResourceAsStream(
embeddedPropertiesFilename );
if ( embeddedPropsStream != null ) {
loadPropsFromStream( embeddedPropsStream, args );
embeddedPropsStream.close( );
}
// Get command line args
String configFilename = RESOURCES.getString( "Launcher.DefaultPropertyFile" );
for ( int n = 0; n < argv.length; n++ ) {
String option = argv[n];
if ( option.startsWith( "--" ) ) {
int equalPos = option.indexOf( '=' );
String paramName = option.substring( 2,
equalPos == -1 ? option.length( ) : equalPos );
if ( equalPos != -1 ) {
args.put( paramName, option.substring( equalPos + 1 ) );
} else {
args.put( paramName, "true" );
}
if ( paramName.equals( "config" ) ) {
configFilename = ( String ) args.get( paramName );
}
} else {
args.put( nonSwitchArgName, option );
}
}
// Load default props if available
File configFile = new File( configFilename );
if ( configFile.exists( ) && configFile.isFile( ) ) {
InputStream inConfig = new FileInputStream( configFile );
loadPropsFromStream( inConfig, args );
inConfig.close( );
initLogger( args );
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.UsingPropertyFile",
configFilename );
} else {
initLogger( args );
}
return args;
}
469 protected static void deployEmbeddedWarfile( Map args ) throws IOException {
String embeddedWarfileName = RESOURCES.getString( "Launcher.EmbeddedWarFile" );
InputStream embeddedWarfile = Launcher.class.getResourceAsStream(
embeddedWarfileName );
if ( embeddedWarfile != null ) {
File tempWarfile = File.createTempFile( "embedded", ".war" ).getAbsoluteFile( );
tempWarfile.getParentFile( ).mkdirs( );
tempWarfile.deleteOnExit( );
String embeddedWebroot = RESOURCES.getString( "Launcher.EmbeddedWebroot" );
File tempWebroot = new File( tempWarfile.getParentFile( ), embeddedWebroot );
tempWebroot.mkdirs( );
Logger.log( Logger.DEBUG, RESOURCES, "Launcher.CopyingEmbeddedWarfile",
tempWarfile.getAbsolutePath( ) );
OutputStream out = new FileOutputStream( tempWarfile, true );
int read = 0;
byte buffer[] = new byte[2048];
while ( ( read = embeddedWarfile.read( buffer ) ) != -1 ) {
out.write( buffer, 0, read );
}
out.close( );
embeddedWarfile.close( );
args.put( "warfile", tempWarfile.getAbsolutePath( ) );
args.put( "webroot", tempWebroot.getAbsolutePath( ) );
args.remove( "webappsDir" );
args.remove( "hostsDir" );
}
}
500 protected static void loadPropsFromStream( InputStream inConfig, Map args ) throws IOException {
Properties props = new Properties( );
props.load( inConfig );
for ( Iterator i = props.keySet( ).iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
if ( !args.containsKey( key.trim( ) ) ) {
args.put( key.trim( ), props.getProperty( key ).trim( ) );
}
}
props.clear( );
}
512 public static void initLogger( Map args ) throws IOException {
// Reset the log level
int logLevel = WebAppConfiguration.intArg( args, "debug", Logger.INFO );
// boolean showThrowingLineNo = WebAppConfiguration.booleanArg( args, "logThrowingLineNo", false );
boolean showThrowingThread = WebAppConfiguration.booleanArg( args, "logThrowingThread", false );
OutputStream logStream = null;
if ( args.get( "logfile" ) != null ) {
logStream = new FileOutputStream( ( String ) args.get( "logfile" ) );
} else if ( WebAppConfiguration.booleanArg( args, "logToStdErr", false ) ) {
logStream = System.err;
} else {
logStream = System.out;
}
// Logger.init( logLevel, logStream, showThrowingLineNo, showThrowingThread );
Logger.init( logLevel, logStream, showThrowingThread );
}
529 protected static void printUsage( ) {
System.out.println( RESOURCES.getString( "Launcher.UsageInstructions",
RESOURCES.getString( "ServerVersion" ) ) );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
/**
* Interface that defines the necessary methods for being a connection listener
* within winstone.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
public interface Listener {
/**
* Interrupts the listener thread. This will trigger a listener shutdown
* once the so timeout has passed.
*/
26 public void destroy( );
/**
* After the listener is loaded and initialized, this starts the thread
*/
31 public boolean start( );
/**
* Called by the request handler thread, because it needs specific setup
* code for this connection's protocol ( ie construction of request/response
* objects, in/out streams, etc ). The iAmFirst variable identifies whether or
* not this is the initial request on on this socket ( ie a keep alive or
* a first-time accept )
*/
40 public void allocateRequestResponse( Socket socket, InputStream inSocket,
41 OutputStream outSocket, RequestHandlerThread handler,
boolean iAmFirst ) throws SocketException, IOException;
/**
* Called by the request handler thread, because it needs specific shutdown
* code for this connection's protocol ( ie releasing input/output streams,
* etc ).
*/
49 public void deallocateRequestResponse( RequestHandlerThread handler,
50 WinstoneRequest req, WinstoneResponse rsp,
51 WinstoneInputStream inData, WinstoneOutputStream outData )
throws IOException;
/**
* Called by the request handler thread, because it needs specific shutdown
* code for this connection's protocol if the keep-alive period expires ( ie
* closing sockets, etc ).The iAmFirst variable identifies whether or
* not this is the initial request on on this socket ( ie a keep alive or
* a first-time accept )
*/
61 public String parseURI( RequestHandlerThread handler, WinstoneRequest req,
62 WinstoneResponse rsp, WinstoneInputStream inData, Socket socket,
boolean iAmFirst ) throws IOException;
/**
* Called by the request handler thread, because it needs specific shutdown
* code for this connection's protocol if the keep-alive period expires ( ie
* closing sockets, etc ).
*/
70 public void releaseSocket( Socket socket, InputStream inSocket,
71 OutputStream outSocket ) throws IOException;
/**
* Tries to wait for extra requests on the same socket. If any are found
* before the timeout expires, it exits with a true, indicating a new
* request is waiting. If the timeout expires, return a false, instructing
* the handler thread to begin shutting down the socket and relase itself.
*/
79 public boolean processKeepAlive( WinstoneRequest request,
80 WinstoneResponse response, InputStream inSocket )
throws IOException, InterruptedException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* A utility class for logging event and status messages. It maintains a
* collection of streams for different types of messages, but any messages with
* unknown or unspecified stream go to the default stream.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Logger.java, v 1.8 2006/11/09 06:01:43 rickknowles Exp $
*/
29 public class Logger {
30 private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
32 public final static String DEFAULT_STREAM = "Winstone";
public static int MIN = 1;
public static int ERROR = 2;
public static int WARNING = 3;
public static int INFO = 5;
public static int SPEED = 6;
public static int DEBUG = 7;
public static int FULL_DEBUG = 8;
public static int MAX = 9;
42 protected static Boolean semaphore = new Boolean( true );
protected static boolean initialised = false;
44 protected static Writer defaultStream;
45 protected static Map namedStreams;
// protected static Collection nullStreams;
protected static int currentDebugLevel;
48 protected final static DateFormat sdfLog = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss" );
protected static boolean showThrowingThread;
/**
* Initialises default streams
*/
54 public static void init( int level ) {
init( level, System.out, false );
}
/**
* Initialises default streams
*/
61 public static void init( int level, OutputStream defaultStream,
boolean showThrowingThreadArg ) {
synchronized ( semaphore ) {
if ( !initialised ) { // recheck in case we were blocking on another init
initialised = false;
currentDebugLevel = level;
namedStreams = new HashMap( );
// nullStreams = new ArrayList( );
initialised = true;
setStream( DEFAULT_STREAM, defaultStream );
showThrowingThread = showThrowingThreadArg;
}
}
}
/**
* Allocates a stream for redirection to a file etc
*/
79 public static void setStream( String name, OutputStream stream ) {
setStream( name, stream != null ? new OutputStreamWriter( stream ) : null );
}
/**
* Allocates a stream for redirection to a file etc
*/
86 public static void setStream( String name, Writer stream ) {
if ( name == null ) {
name = DEFAULT_STREAM;
}
if ( !initialised ) {
init( INFO );
}
synchronized ( semaphore ) {
if ( name.equals( DEFAULT_STREAM ) ) {
defaultStream = stream;
} else if ( stream == null ) {
namedStreams.remove( name );
} else {
namedStreams.put( name, stream );
}
}
}
/**
* Forces a flush of the contents to file, display, etc
*/
107 public static void flush( String name ) {
if ( !initialised ) {
init( INFO );
}
Writer stream = getStreamByName( name );
if ( stream != null ) {
try {stream.flush( );} catch ( IOException err ) {}
}
}
118 private static Writer getStreamByName( String streamName ) {
if ( ( streamName != null ) && streamName.equals( DEFAULT_STREAM ) ) {
// As long as the stream has not been nulled, assign the default if not found
synchronized ( semaphore ) {
Writer stream = ( Writer ) namedStreams.get( streamName );
if ( ( stream == null ) && !namedStreams.containsKey( streamName ) ) {
stream = defaultStream;
}
return stream;
}
} else {
return defaultStream;
}
}
134 public static void setCurrentDebugLevel( int level ) {
if ( !initialised ) {
init( level );
} else synchronized ( semaphore ) {
currentDebugLevel = level;
}
}
/**
* Writes a log message to the requested stream, and immediately flushes
* the contents of the stream.
*/
146 private static void logInternal( String streamName, String message, Throwable error ) {
if ( !initialised ) {
init( INFO );
}
Writer stream = getStreamByName( streamName );
if ( stream != null ) {
Writer fullMessage = new StringWriter( );
String date = null;
synchronized ( sdfLog ) {
date = sdfLog.format( new Date( ) );
}
try {
fullMessage.write( "[" );
fullMessage.write( streamName );
fullMessage.write( " " );
fullMessage.write( date );
fullMessage.write( "] - " );
if ( showThrowingThread ) {
fullMessage.write( "[" );
fullMessage.write( Thread.currentThread( ).getName( ) );
fullMessage.write( "] - " );
}
fullMessage.write( message );
if ( error != null ) {
fullMessage.write( LINE_SEPARATOR );
PrintWriter pw = new PrintWriter( fullMessage );
error.printStackTrace( pw );
pw.flush( );
}
fullMessage.write( LINE_SEPARATOR );
stream.write( fullMessage.toString( ) );
stream.flush( );
} catch ( IOException err ) {
System.err.println( Launcher.RESOURCES.getString( "Logger.StreamWriteError", message ) );
err.printStackTrace( System.err );
}
}
}
188 public static void log( int level, WinstoneResourceBundle resources,
189 String messageKey ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( DEFAULT_STREAM, resources.getString( messageKey ), null );
}
}
197 public static void log( int level, WinstoneResourceBundle resources,
198 String messageKey, Throwable error ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( DEFAULT_STREAM, resources.getString( messageKey ), error );
}
}
206 public static void log( int level, WinstoneResourceBundle resources,
207 String messageKey, String param ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( DEFAULT_STREAM, resources.getString( messageKey, param ), null );
}
}
215 public static void log( int level, WinstoneResourceBundle resources,
216 String messageKey, String params[] ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( DEFAULT_STREAM, resources.getString( messageKey, params ), null );
}
}
224 public static void log( int level, WinstoneResourceBundle resources,
225 String messageKey, String param, Throwable error ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( DEFAULT_STREAM, resources.getString( messageKey, param ), error );
}
}
233 public static void log( int level, WinstoneResourceBundle resources,
234 String messageKey, String params[], Throwable error ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( DEFAULT_STREAM, resources.getString( messageKey, params ), error );
}
}
242 public static void log( int level, WinstoneResourceBundle resources,
243 String streamName, String messageKey, String params[], Throwable error ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( streamName, resources.getString( messageKey, params ), error );
}
}
251 public static void logDirectMessage( int level, String streamName, String message,
252 Throwable error ) {
if ( currentDebugLevel < level ) {
return;
} else {
logInternal( streamName, message, error );
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
/**
* Encapsulates the parsing of URL patterns, as well as the mapping of a
* url pattern to a servlet instance
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Mapping.java, v 1.9 2007/04/23 02:55:35 rickknowles Exp $
*/
16 public class Mapping implements java.util.Comparator {
public static final int EXACT_PATTERN = 1;
public static final int FOLDER_PATTERN = 2;
public static final int EXTENSION_PATTERN = 3;
public static final int DEFAULT_SERVLET = 4;
22 public static final String STAR = "*";
23 public static final String SLASH = "/";
25 private String urlPattern;
26 private String linkName; // used to map filters to a specific servlet by
// name
28 private String mappedTo;
private int patternType;
private boolean isPatternFirst; // ie is this a blah* pattern, not *blah
// ( extensions only )
33 protected Mapping( String mappedTo ) {
this.mappedTo = mappedTo;
}
/**
* Factory constructor method - this parses the url pattern into pieces we can use to match
* against incoming URLs.
*/
41 public static Mapping createFromURL( String mappedTo, String pattern ) {
if ( ( pattern == null ) || ( mappedTo == null ) )
throw new WinstoneException( Launcher.RESOURCES.getString(
"Mapping.InvalidMount", new String[] { mappedTo, pattern } ) );
// Compatibility hacks - add a leading slash if one is not found and not
// an extension mapping
if ( !pattern.equals( "" ) && !pattern.startsWith( STAR ) &&
!pattern.startsWith( SLASH ) ) {
pattern = SLASH + pattern;
} else if ( pattern.equals( STAR ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "Mapping.RewritingStarMount" );
pattern = SLASH + STAR;
}
Mapping me = new Mapping( mappedTo );
int firstStarPos = pattern.indexOf( STAR );
int lastStarPos = pattern.lastIndexOf( STAR );
int patternLength = pattern.length( );
// check for default servlet, ie mapping = exactly /
if ( pattern.equals( SLASH ) ) {
me.urlPattern = "";
me.patternType = DEFAULT_SERVLET;
}
else if ( firstStarPos == -1 ) {
me.urlPattern = pattern;
me.patternType = EXACT_PATTERN;
}
// > 1 star = error
else if ( firstStarPos != lastStarPos )
throw new WinstoneException( Launcher.RESOURCES.getString(
"Mapping.InvalidMount", new String[] { mappedTo, pattern } ) );
// check for folder style mapping ( ends in /* )
else if ( pattern.indexOf( SLASH + STAR ) == ( patternLength - ( SLASH + STAR ).length( ) ) ) {
me.urlPattern = pattern.substring( 0, pattern.length( )
- ( SLASH + STAR ).length( ) );
me.patternType = FOLDER_PATTERN;
}
// check for non-extension match
else if ( pattern.indexOf( SLASH ) != -1 )
throw new WinstoneException( Launcher.RESOURCES.getString(
"Mapping.InvalidMount", new String[] { mappedTo, pattern } ) );
// check for extension match at the beginning ( eg *blah )
else if ( firstStarPos == 0 ) {
me.urlPattern = pattern.substring( STAR.length( ) );
me.patternType = EXTENSION_PATTERN;
me.isPatternFirst = false;
}
// check for extension match at the end ( eg blah* )
else if ( firstStarPos == ( patternLength - STAR.length( ) ) ) {
me.urlPattern = pattern.substring( 0, patternLength - STAR.length( ) );
me.patternType = EXTENSION_PATTERN;
me.isPatternFirst = true;
} else
throw new WinstoneException( Launcher.RESOURCES.getString(
"Mapping.InvalidMount", new String[] { mappedTo, pattern } ) );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "Mapping.MappedPattern",
new String[] { mappedTo, pattern } );
return me;
}
/**
* Factory constructor method - this turns a servlet name into a mapping element
*/
113 public static Mapping createFromLink( String mappedTo, String linkName ) {
if ( ( linkName == null ) || ( mappedTo == null ) )
throw new WinstoneException( Launcher.RESOURCES.getString(
"Mapping.InvalidLink", new String[] { mappedTo, linkName } ) );
Mapping me = new Mapping( mappedTo );
me.linkName = linkName;
return me;
}
123 public int getPatternType( ) {
return this.patternType;
}
127 public String getUrlPattern( ) {
return this.urlPattern;
}
131 public String getMappedTo( ) {
return this.mappedTo;
}
135 public String getLinkName( ) {
return this.linkName;
}
/**
* Try to match this pattern against the incoming url
*
* @param inputPattern The URL we want to check for a match
* @param servletPath An empty stringbuffer for the servletPath of a successful match
* @param pathInfo An empty stringbuffer for the pathInfo of a successful match
* @return true if the match is successful
*/
147 public boolean match( String inputPattern, StringBuffer servletPath,
148 StringBuffer pathInfo ) {
switch ( this.patternType ) {
case FOLDER_PATTERN:
if ( inputPattern.startsWith( this.urlPattern + '/' ) ||
inputPattern.equals( this.urlPattern ) ) {
if ( servletPath != null )
servletPath.append( WinstoneRequest.decodeURLToken( this.urlPattern ) );
if ( pathInfo != null )
pathInfo.append( WinstoneRequest.decodeURLToken( inputPattern.substring( this.urlPattern.length( ) ) ) );
return true;
} else
return false;
case EXTENSION_PATTERN:
// Strip down to the last item in the path
int slashPos = inputPattern.lastIndexOf( SLASH );
if ( ( slashPos == -1 ) || ( slashPos == inputPattern.length( ) - 1 ) )
return false;
String fileName = inputPattern.substring( slashPos + 1 );
if ( ( this.isPatternFirst && fileName.startsWith( this.urlPattern ) )
|| ( !this.isPatternFirst && fileName.endsWith( this.urlPattern ) ) ) {
if ( servletPath != null )
servletPath.append( WinstoneRequest.decodeURLToken( inputPattern ) );
return true;
} else
return false;
case EXACT_PATTERN:
if ( inputPattern.equals( this.urlPattern ) ) {
if ( servletPath != null )
servletPath.append( WinstoneRequest.decodeURLToken( inputPattern ) );
return true;
} else
return false;
case DEFAULT_SERVLET:
if ( servletPath != null )
servletPath.append( WinstoneRequest.decodeURLToken( inputPattern ) );
return true;
default:
return false;
}
}
/**
* Used to compare two url patterns. Always sorts so that lowest pattern
* type then longest path come first.
*/
197 public int compare( Object objOne, Object objTwo ) {
Mapping one = ( Mapping ) objOne;
Mapping two = ( Mapping ) objTwo;
Integer intOne = new Integer( one.getPatternType( ) );
Integer intTwo = new Integer( two.getPatternType( ) );
int order = -1 * intOne.compareTo( intTwo );
if ( order != 0 ) {
return order;
}
if ( one.getLinkName( ) != null ) {
// servlet name mapping - just alphabetical sort
return one.getLinkName( ).compareTo( two.getLinkName( ) );
} else {
return -1 * one.getUrlPattern( ).compareTo( two.getUrlPattern( ) );
}
}
215 public String toString( ) {
return this.linkName != null ? "Link:" + this.linkName
: "URLPattern:type=" + this.patternType + ", pattern="
+ this.urlPattern;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Holds the object pooling code for Winstone. Presently this is only responses
* and requests, but may increase.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ObjectPool.java, v 1.9 2006/11/18 14:56:59 rickknowles Exp $
*/
24 public class ObjectPool implements Runnable {
private static final long FLUSH_PERIOD = 60000L;
private int STARTUP_REQUEST_HANDLERS_IN_POOL = 5;
private int MAX_IDLE_REQUEST_HANDLERS_IN_POOL = 50;
private int MAX_REQUEST_HANDLERS_IN_POOL = 1000;
private long RETRY_PERIOD = 1000;
private int START_REQUESTS_IN_POOL = 10;
private int MAX_REQUESTS_IN_POOL = 1000;
private int START_RESPONSES_IN_POOL = 10;
private int MAX_RESPONSES_IN_POOL = 1000;
35 private List unusedRequestHandlerThreads;
36 private List usedRequestHandlerThreads;
37 private List usedRequestPool;
38 private List unusedRequestPool;
39 private List usedResponsePool;
40 private List unusedResponsePool;
41 private Object requestHandlerSemaphore = new Boolean( true );
42 private Object requestPoolSemaphore = new Boolean( true );
43 private Object responsePoolSemaphore = new Boolean( true );
private int threadIndex = 0;
private boolean simulateModUniqueId;
private boolean saveSessions;
48 private Thread thread;
/**
* Constructs an instance of the object pool, including handlers, requests
* and responses
*/
54 public ObjectPool( Map args ) throws IOException {
this.simulateModUniqueId = WebAppConfiguration.booleanArg( args, "simulateModUniqueId", false );
this.saveSessions = WebAppConfiguration.useSavedSessions( args );
// Build the initial pool of handler threads
this.unusedRequestHandlerThreads = new ArrayList( );
this.usedRequestHandlerThreads = new ArrayList( );
// Build the request/response pools
this.usedRequestPool = new ArrayList( );
this.usedResponsePool = new ArrayList( );
this.unusedRequestPool = new ArrayList( );
this.unusedResponsePool = new ArrayList( );
// Get handler pool options
if ( args.get( "handlerCountStartup" ) != null ) {
STARTUP_REQUEST_HANDLERS_IN_POOL = Integer.parseInt( ( String ) args
.get( "handlerCountStartup" ) );
}
if ( args.get( "handlerCountMax" ) != null ) {
MAX_IDLE_REQUEST_HANDLERS_IN_POOL = Integer.parseInt( ( String ) args
.get( "handlerCountMax" ) );
}
if ( args.get( "handlerCountMaxIdle" ) != null ) {
MAX_IDLE_REQUEST_HANDLERS_IN_POOL = Integer.parseInt( ( String ) args
.get( "handlerCountMaxIdle" ) );
}
// Start the base set of handler threads
for ( int n = 0; n < STARTUP_REQUEST_HANDLERS_IN_POOL; n++ ) {
this.unusedRequestHandlerThreads
.add( new RequestHandlerThread( this,
this.threadIndex++, this.simulateModUniqueId,
this.saveSessions ) );
}
// Initialise the request/response pools
for ( int n = 0; n < START_REQUESTS_IN_POOL; n++ ) {
this.unusedRequestPool.add( new WinstoneRequest( ) );
}
for ( int n = 0; n < START_RESPONSES_IN_POOL; n++ ) {
this.unusedResponsePool.add( new WinstoneResponse( ) );
}
this.thread = new Thread( this, "WinstoneObjectPoolMgmt" );
this.thread.setDaemon( true );
this.thread.start( );
}
103 public void run( ) {
boolean interrupted = false;
while ( !interrupted ) {
try {
Thread.sleep( FLUSH_PERIOD );
removeUnusedRequestHandlers( );
} catch ( InterruptedException err ) {
interrupted = true;
}
}
this.thread = null;
}
116 private void removeUnusedRequestHandlers( ) {
// Check max idle requestHandler count
synchronized ( this.requestHandlerSemaphore ) {
// If we have too many idle request handlers
while ( this.unusedRequestHandlerThreads.size( ) > MAX_IDLE_REQUEST_HANDLERS_IN_POOL ) {
RequestHandlerThread rh = ( RequestHandlerThread ) this.unusedRequestHandlerThreads.get( 0 );
rh.destroy( );
this.unusedRequestHandlerThreads.remove( rh );
}
}
}
128 public void destroy( ) {
synchronized ( this.requestHandlerSemaphore ) {
Collection usedHandlers = new ArrayList( this.usedRequestHandlerThreads );
for ( Iterator i = usedHandlers.iterator( ); i.hasNext( ); )
releaseRequestHandler( ( RequestHandlerThread ) i.next( ) );
Collection unusedHandlers = new ArrayList( this.unusedRequestHandlerThreads );
for ( Iterator i = unusedHandlers.iterator( ); i.hasNext( ); )
( ( RequestHandlerThread ) i.next( ) ).destroy( );
this.unusedRequestHandlerThreads.clear( );
}
if ( this.thread != null ) {
this.thread.interrupt( );
}
}
/**
* Once the socket request comes in, this method is called. It reserves a
* request handler, then delegates the socket to that class. When it
* finishes, the handler is released back into the pool.
*/
148 public void handleRequest( Socket socket, Listener listener )
throws IOException, InterruptedException {
RequestHandlerThread rh = null;
synchronized ( this.requestHandlerSemaphore ) {
// If we have any spare, get it from the pool
int unused = this.unusedRequestHandlerThreads.size( );
if ( unused > 0 ) {
rh = ( RequestHandlerThread ) this.unusedRequestHandlerThreads.remove( unused - 1 );
this.usedRequestHandlerThreads.add( rh );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.UsingRHPoolThread", new String[] {
"" + this.usedRequestHandlerThreads.size( ),
"" + this.unusedRequestHandlerThreads.size( ) } );
}
// If we are out ( and not over our limit ), allocate a new one
else if ( this.usedRequestHandlerThreads.size( ) < MAX_REQUEST_HANDLERS_IN_POOL ) {
rh = new RequestHandlerThread( this,
this.threadIndex++, this.simulateModUniqueId,
this.saveSessions );
this.usedRequestHandlerThreads.add( rh );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.NewRHPoolThread", new String[] {
"" + this.usedRequestHandlerThreads.size( ),
"" + this.unusedRequestHandlerThreads.size( ) } );
}
// otherwise throw fail message - we've blown our limit
else {
// Possibly insert a second chance here ? Delay and one retry ?
// Remember to release the lock first
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"ObjectPool.NoRHPoolThreadsRetry" );
// socket.close( );
// throw new UnavailableException( "NoHandlersAvailable" );
}
}
if ( rh != null )
rh.commenceRequestHandling( socket, listener );
else {
// Sleep for a set period and try again from the pool
Thread.sleep( RETRY_PERIOD );
synchronized ( this.requestHandlerSemaphore ) {
if ( this.usedRequestHandlerThreads.size( ) < MAX_REQUEST_HANDLERS_IN_POOL ) {
rh = new RequestHandlerThread( this,
this.threadIndex++, this.simulateModUniqueId,
this.saveSessions );
this.usedRequestHandlerThreads.add( rh );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.NewRHPoolThread", new String[] {
"" + this.usedRequestHandlerThreads.size( ),
"" + this.unusedRequestHandlerThreads.size( ) } );
}
}
if ( rh != null )
rh.commenceRequestHandling( socket, listener );
else {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"ObjectPool.NoRHPoolThreads" );
socket.close( );
}
}
}
/**
* Release the handler back into the pool
*/
217 public void releaseRequestHandler( RequestHandlerThread rh ) {
synchronized ( this.requestHandlerSemaphore ) {
this.usedRequestHandlerThreads.remove( rh );
this.unusedRequestHandlerThreads.add( rh );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.ReleasingRHPoolThread", new String[] {
"" + this.usedRequestHandlerThreads.size( ),
"" + this.unusedRequestHandlerThreads.size( ) } );
}
}
/**
* An attempt at pooling request objects for reuse.
*/
231 public WinstoneRequest getRequestFromPool( ) throws IOException {
WinstoneRequest req = null;
synchronized ( this.requestPoolSemaphore ) {
// If we have any spare, get it from the pool
int unused = this.unusedRequestPool.size( );
if ( unused > 0 ) {
req = ( WinstoneRequest ) this.unusedRequestPool.remove( unused - 1 );
this.usedRequestPool.add( req );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.UsingRequestFromPool", ""
+ this.unusedRequestPool.size( ) );
}
// If we are out, allocate a new one
else if ( this.usedRequestPool.size( ) < MAX_REQUESTS_IN_POOL ) {
req = new WinstoneRequest( );
this.usedRequestPool.add( req );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.NewRequestForPool", ""
+ this.usedRequestPool.size( ) );
} else
throw new WinstoneException( Launcher.RESOURCES
.getString( "ObjectPool.PoolRequestLimitExceeded" ) );
}
return req;
}
257 public void releaseRequestToPool( WinstoneRequest req ) {
req.cleanUp( );
synchronized ( this.requestPoolSemaphore ) {
this.usedRequestPool.remove( req );
this.unusedRequestPool.add( req );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.RequestReleased", ""
+ this.unusedRequestPool.size( ) );
}
}
/**
* An attempt at pooling request objects for reuse.
*/
271 public WinstoneResponse getResponseFromPool( ) throws IOException {
WinstoneResponse rsp = null;
synchronized ( this.responsePoolSemaphore ) {
// If we have any spare, get it from the pool
int unused = this.unusedResponsePool.size( );
if ( unused > 0 ) {
rsp = ( WinstoneResponse ) this.unusedResponsePool.remove( unused - 1 );
this.usedResponsePool.add( rsp );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.UsingResponseFromPool", ""
+ this.unusedResponsePool.size( ) );
}
// If we are out, allocate a new one
else if ( this.usedResponsePool.size( ) < MAX_RESPONSES_IN_POOL ) {
rsp = new WinstoneResponse( );
this.usedResponsePool.add( rsp );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.NewResponseForPool", ""
+ this.usedResponsePool.size( ) );
} else
throw new WinstoneException( Launcher.RESOURCES
.getString( "ObjectPool.PoolResponseLimitExceeded" ) );
}
return rsp;
}
297 public void releaseResponseToPool( WinstoneResponse rsp ) {
rsp.cleanUp( );
synchronized ( this.responsePoolSemaphore ) {
this.usedResponsePool.remove( rsp );
this.unusedResponsePool.add( rsp );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ObjectPool.ResponseReleased", ""
+ this.unusedResponsePool.size( ) );
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
/**
* This class implements both the RequestDispatcher and FilterChain components. On
* the first call to include( ) or forward( ), it starts the filter chain execution
* if one exists. On the final doFilter( ) or if there is no chain, we call the include( )
* or forward( ) again, and the servlet is executed.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: RequestDispatcher.java, v 1.18 2007/04/23 02:55:35 rickknowles Exp $
*/
29 public class RequestDispatcher implements javax.servlet.RequestDispatcher,
javax.servlet.FilterChain {
32 static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
33 static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
34 static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
35 static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
36 static final String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
38 static final String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
39 static final String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
40 static final String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
41 static final String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
42 static final String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
44 static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
45 static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
46 static final String ERROR_MESSAGE = "javax.servlet.error.message";
47 static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
48 static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
49 static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
51 private WebAppConfiguration webAppConfig;
52 private ServletConfiguration servletConfig;
54 private String servletPath;
55 private String pathInfo;
56 private String queryString;
57 private String requestURI;
59 private Integer errorStatusCode;
60 private Throwable errorException;
61 private String errorSummaryMessage;
63 private AuthenticationHandler authHandler;
65 private Mapping forwardFilterPatterns[];
66 private Mapping includeFilterPatterns[];
67 private FilterConfiguration matchingFilters[];
private int matchingFiltersEvaluated;
70 private Boolean doInclude;
private boolean isErrorDispatch;
private boolean useRequestAttributes;
74 private WebAppConfiguration includedWebAppConfig;
75 private ServletConfiguration includedServletConfig;
/**
* Constructor. This initializes the filter chain and sets up the details
* needed to handle a servlet excecution, such as security constraints,
* filters, etc.
*/
82 public RequestDispatcher( WebAppConfiguration webAppConfig, ServletConfiguration servletConfig ) {
this.servletConfig = servletConfig;
this.webAppConfig = webAppConfig;
this.matchingFiltersEvaluated = 0;
}
89 public void setForNamedDispatcher( Mapping forwardFilterPatterns[],
90 Mapping includeFilterPatterns[] ) {
this.forwardFilterPatterns = forwardFilterPatterns;
this.includeFilterPatterns = includeFilterPatterns;
this.matchingFilters = null; // set after the call to forward or include
this.useRequestAttributes = false;
this.isErrorDispatch = false;
}
98 public void setForURLDispatcher( String servletPath, String pathInfo,
99 String queryString, String requestURIInsideWebapp,
100 Mapping forwardFilterPatterns[], Mapping includeFilterPatterns[] ) {
this.servletPath = servletPath;
this.pathInfo = pathInfo;
this.queryString = queryString;
this.requestURI = requestURIInsideWebapp;
this.forwardFilterPatterns = forwardFilterPatterns;
this.includeFilterPatterns = includeFilterPatterns;
this.matchingFilters = null; // set after the call to forward or include
this.useRequestAttributes = true;
this.isErrorDispatch = false;
}
113 public void setForErrorDispatcher( String servletPath, String pathInfo,
114 String queryString, int statusCode, String summaryMessage,
115 Throwable exception, String errorHandlerURI,
116 Mapping errorFilterPatterns[] ) {
this.servletPath = servletPath;
this.pathInfo = pathInfo;
this.queryString = queryString;
this.requestURI = errorHandlerURI;
this.errorStatusCode = new Integer( statusCode );
this.errorException = exception;
this.errorSummaryMessage = summaryMessage;
this.matchingFilters = getMatchingFilters( errorFilterPatterns, this.webAppConfig,
servletPath + ( pathInfo == null ? "" : pathInfo ),
getName( ), "ERROR", ( servletPath != null ) );
this.useRequestAttributes = true;
this.isErrorDispatch = true;
}
132 public void setForInitialDispatcher( String servletPath, String pathInfo,
133 String queryString, String requestURIInsideWebapp, Mapping requestFilterPatterns[],
134 AuthenticationHandler authHandler ) {
this.servletPath = servletPath;
this.pathInfo = pathInfo;
this.queryString = queryString;
this.requestURI = requestURIInsideWebapp;
this.authHandler = authHandler;
this.matchingFilters = getMatchingFilters( requestFilterPatterns, this.webAppConfig,
servletPath + ( pathInfo == null ? "" : pathInfo ),
getName( ), "REQUEST", ( servletPath != null ) );
this.useRequestAttributes = false;
this.isErrorDispatch = false;
}
147 public String getName( ) {
return this.servletConfig.getServletName( );
}
/**
* Includes the execution of a servlet into the current request
*
* Note this method enters itself twice: once with the initial call, and once again
* when all the filters have completed.
*/
157 public void include( ServletRequest request, ServletResponse response )
throws ServletException, IOException {
// On the first call, log and initialise the filter chain
if ( this.doInclude == null ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"RequestDispatcher.IncludeMessage", new String[] {
getName( ), this.requestURI } );
WinstoneRequest wr = getUnwrappedRequest( request );
// Add the query string to the included query string stack
wr.addIncludeQueryParameters( this.queryString );
// Set request attributes
if ( useRequestAttributes ) {
wr.addIncludeAttributes( this.webAppConfig.getContextPath( ) + this.requestURI,
this.webAppConfig.getContextPath( ), this.servletPath, this.pathInfo, this.queryString );
}
// Add another include buffer to the response stack
WinstoneResponse wresp = getUnwrappedResponse( response );
wresp.startIncludeBuffer( );
this.includedServletConfig = wr.getServletConfig( );
this.includedWebAppConfig = wr.getWebAppConfig( );
wr.setServletConfig( this.servletConfig );
wr.setWebAppConfig( this.webAppConfig );
wresp.setWebAppConfig( this.webAppConfig );
this.doInclude = Boolean.TRUE;
}
if ( this.matchingFilters == null ) {
this.matchingFilters = getMatchingFilters( this.includeFilterPatterns, this.webAppConfig,
this.servletPath + ( this.pathInfo == null ? "" : this.pathInfo ),
getName( ), "INCLUDE", ( this.servletPath != null ) );
}
try {
// Make sure the filter chain is exhausted first
if ( this.matchingFiltersEvaluated < this.matchingFilters.length ) {
doFilter( request, response );
finishInclude( request, response );
} else {
try {
this.servletConfig.execute( request, response,
this.webAppConfig.getContextPath( ) + this.requestURI );
} finally {
if ( this.matchingFilters.length == 0 ) {
finishInclude( request, response );
}
}
}
} catch ( Throwable err ) {
finishInclude( request, response );
if ( err instanceof ServletException ) {
throw ( ServletException ) err;
} else if ( err instanceof IOException ) {
throw ( IOException ) err;
} else if ( err instanceof Error ) {
throw ( Error ) err;
} else {
throw ( RuntimeException ) err;
}
}
}
222 private void finishInclude( ServletRequest request, ServletResponse response )
throws IOException {
WinstoneRequest wr = getUnwrappedRequest( request );
wr.removeIncludeQueryString( );
// Set request attributes
if ( useRequestAttributes ) {
wr.removeIncludeAttributes( );
}
// Remove the include buffer from the response stack
WinstoneResponse wresp = getUnwrappedResponse( response );
wresp.finishIncludeBuffer( );
if ( this.includedServletConfig != null ) {
wr.setServletConfig( this.includedServletConfig );
this.includedServletConfig = null;
}
if ( this.includedWebAppConfig != null ) {
wr.setWebAppConfig( this.includedWebAppConfig );
wresp.setWebAppConfig( this.includedWebAppConfig );
this.includedWebAppConfig = null;
}
}
/**
* Forwards to another servlet, and when it's finished executing that other
* servlet, cut off execution.
*
* Note this method enters itself twice: once with the initial call, and once again
* when all the filters have completed.
*/
254 public void forward( ServletRequest request, ServletResponse response )
throws ServletException, IOException {
// Only on the first call to forward, we should set any forwarding attributes
if ( this.doInclude == null ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"RequestDispatcher.ForwardMessage", new String[] {
getName( ), this.requestURI } );
if ( response.isCommitted( ) ) {
throw new IllegalStateException( Launcher.RESOURCES.getString(
"RequestDispatcher.ForwardCommitted" ) );
}
WinstoneRequest req = getUnwrappedRequest( request );
WinstoneResponse rsp = getUnwrappedResponse( response );
// Clear the include stack if one has been accumulated
rsp.resetBuffer( );
req.clearIncludeStackForForward( );
rsp.clearIncludeStackForForward( );
// Set request attributes ( because it's the first step in the filter chain of a forward or error )
if ( useRequestAttributes ) {
req.setAttribute( FORWARD_REQUEST_URI, req.getRequestURI( ) );
req.setAttribute( FORWARD_CONTEXT_PATH, req.getContextPath( ) );
req.setAttribute( FORWARD_SERVLET_PATH, req.getServletPath( ) );
req.setAttribute( FORWARD_PATH_INFO, req.getPathInfo( ) );
req.setAttribute( FORWARD_QUERY_STRING, req.getQueryString( ) );
if ( this.isErrorDispatch ) {
req.setAttribute( ERROR_REQUEST_URI, req.getRequestURI( ) );
req.setAttribute( ERROR_STATUS_CODE, this.errorStatusCode );
req.setAttribute( ERROR_MESSAGE,
errorSummaryMessage != null ? errorSummaryMessage : "" );
if ( req.getServletConfig( ) != null ) {
req.setAttribute( ERROR_SERVLET_NAME, req.getServletConfig( ).getServletName( ) );
}
if ( this.errorException != null ) {
req.setAttribute( ERROR_EXCEPTION_TYPE, this.errorException.getClass( ) );
req.setAttribute( ERROR_EXCEPTION, this.errorException );
}
// Revert back to the original request and response
rsp.setErrorStatusCode( this.errorStatusCode.intValue( ) );
request = req;
response = rsp;
}
}
req.setServletPath( this.servletPath );
req.setPathInfo( this.pathInfo );
req.setRequestURI( this.webAppConfig.getContextPath( ) + this.requestURI );
req.setForwardQueryString( this.queryString );
req.setWebAppConfig( this.webAppConfig );
req.setServletConfig( this.servletConfig );
req.setRequestAttributeListeners( this.webAppConfig.getRequestAttributeListeners( ) );
rsp.setWebAppConfig( this.webAppConfig );
// Forwards haven't set up the filter pattern set yet
if ( this.matchingFilters == null ) {
this.matchingFilters = getMatchingFilters( this.forwardFilterPatterns, this.webAppConfig,
this.servletPath + ( this.pathInfo == null ? "" : this.pathInfo ),
getName( ), "FORWARD", ( this.servletPath != null ) );
}
// Otherwise we are an initial or error dispatcher, so check security if initial -
// if we should not continue, return
else if ( !this.isErrorDispatch && !continueAfterSecurityCheck( request, response ) ) {
return;
}
this.doInclude = Boolean.FALSE;
}
// Make sure the filter chain is exhausted first
boolean outsideFilter = ( this.matchingFiltersEvaluated == 0 );
if ( this.matchingFiltersEvaluated < this.matchingFilters.length ) {
doFilter( request, response );
} else {
this.servletConfig.execute( request, response, this.webAppConfig.getContextPath( ) + this.requestURI );
}
// Stop any output after the final filter has been executed ( e.g. from forwarding servlet )
if ( outsideFilter ) {
WinstoneResponse rsp = getUnwrappedResponse( response );
rsp.flushBuffer( );
rsp.getWinstoneOutputStream( ).setClosed( true );
}
}
345 private boolean continueAfterSecurityCheck( ServletRequest request,
346 ServletResponse response ) throws IOException, ServletException {
// Evaluate security constraints
if ( this.authHandler != null ) {
return this.authHandler.processAuthentication( request, response,
this.servletPath + ( this.pathInfo == null ? "" : this.pathInfo ) );
} else {
return true;
}
}
/**
* Handles the processing of the chain of filters, so that we process them
* all, then pass on to the main servlet
*/
360 public void doFilter( ServletRequest request, ServletResponse response )
throws ServletException, IOException {
// Loop through the filter mappings until we hit the end
while ( this.matchingFiltersEvaluated < this.matchingFilters.length ) {
FilterConfiguration filter = this.matchingFilters[this.matchingFiltersEvaluated++];
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"RequestDispatcher.ExecutingFilter", filter.getFilterName( ) );
filter.execute( request, response, this );
return;
}
// Forward / include as requested in the beginning
if ( this.doInclude == null )
return; // will never happen, because we can't call doFilter before forward/include
else if ( this.doInclude.booleanValue( ) )
include( request, response );
else
forward( request, response );
}
/**
* Caches the filter matching, so that if the same URL is requested twice, we don't recalculate the
* filter matching every time.
*/
385 private static FilterConfiguration[] getMatchingFilters( Mapping filterPatterns[],
386 WebAppConfiguration webAppConfig, String fullPath, String servletName,
387 String filterChainType, boolean isURLBasedMatch ) {
String cacheKey = null;
if ( isURLBasedMatch ) {
cacheKey = filterChainType + ":URI:" + fullPath;
} else {
cacheKey = filterChainType + ":Servlet:" + servletName;
}
FilterConfiguration matchingFilters[] = null;
Map cache = webAppConfig.getFilterMatchCache( );
synchronized ( cache ) {
matchingFilters = ( FilterConfiguration [] ) cache.get( cacheKey );
if ( matchingFilters == null ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestDispatcher.CalcFilterChain", cacheKey );
List outFilters = new ArrayList( );
for ( int n = 0; n < filterPatterns.length; n++ ) {
// Get the pattern and eval it, bumping up the eval'd count
Mapping filterPattern = filterPatterns[n];
// If the servlet name matches this name, execute it
if ( ( filterPattern.getLinkName( ) != null )
&& ( filterPattern.getLinkName( ).equals( servletName ) ||
filterPattern.getLinkName( ).equals( "*" ) ) ) {
outFilters.add( webAppConfig.getFilters( ).get( filterPattern.getMappedTo( ) ) );
}
// If the url path matches this filters mappings
else if ( ( filterPattern.getLinkName( ) == null ) && isURLBasedMatch
&& filterPattern.match( fullPath, null, null ) ) {
outFilters.add( webAppConfig.getFilters( ).get( filterPattern.getMappedTo( ) ) );
}
}
matchingFilters = ( FilterConfiguration [] ) outFilters.toArray( new FilterConfiguration[0] );
cache.put( cacheKey, matchingFilters );
} else {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestDispatcher.UseCachedFilterChain", cacheKey );
}
}
return matchingFilters;
}
/**
* Unwrap back to the original container allocated request object
*/
432 protected WinstoneRequest getUnwrappedRequest( ServletRequest request ) {
ServletRequest workingRequest = request;
while ( workingRequest instanceof ServletRequestWrapper ) {
workingRequest = ( ( ServletRequestWrapper ) workingRequest ).getRequest( );
}
return ( WinstoneRequest ) workingRequest;
}
/**
* Unwrap back to the original container allocated response object
*/
443 protected WinstoneResponse getUnwrappedResponse( ServletResponse response ) {
ServletResponse workingResponse = response;
while ( workingResponse instanceof ServletResponseWrapper ) {
workingResponse = ( ( ServletResponseWrapper ) workingResponse ).getResponse( );
}
return ( WinstoneResponse ) workingResponse;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
/**
* The threads to which incoming requests get allocated.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: RequestHandlerThread.java, v 1.21 2007/04/23 02:55:35 rickknowles Exp $
*/
26 public class RequestHandlerThread implements Runnable {
27 private Thread thread;
28 private ObjectPool objectPool;
29 private WinstoneInputStream inData;
30 private WinstoneOutputStream outData;
31 private WinstoneRequest req;
32 private WinstoneResponse rsp;
33 private Listener listener;
34 private Socket socket;
35 private String threadName;
private long requestStartTime;
private boolean simulateModUniqueId;
private boolean saveSessions;
// private Object processingMonitor = new Boolean( true );
/**
* Constructor - this is called by the handler pool, and just sets up for
* when a real request comes along.
*/
45 public RequestHandlerThread( ObjectPool objectPool, int threadIndex,
boolean simulateModUniqueId, boolean saveSessions ) {
this.objectPool = objectPool;
this.simulateModUniqueId = simulateModUniqueId;
this.saveSessions = saveSessions;
this.threadName = Launcher.RESOURCES.getString(
"RequestHandlerThread.ThreadName", "" + threadIndex );
// allocate a thread to run on this object
this.thread = new Thread( this, threadName );
this.thread.setDaemon( true );
}
/**
* The main thread execution code.
*/
61 public void run( ) {
boolean interrupted = false;
while ( !interrupted ) {
// Start request processing
InputStream inSocket = null;
OutputStream outSocket = null;
boolean iAmFirst = true;
try {
// Get input/output streams
inSocket = socket.getInputStream( );
outSocket = socket.getOutputStream( );
// The keep alive loop - exiting from here means the connection has closed
boolean continueFlag = true;
while ( continueFlag && !interrupted ) {
try {
long requestId = System.currentTimeMillis( );
this.listener.allocateRequestResponse( socket, inSocket,
outSocket, this, iAmFirst );
if ( this.req == null ) {
// Dead request - happens sometimes with ajp13 - discard
this.listener.deallocateRequestResponse( this, req,
rsp, inData, outData );
continue;
}
String servletURI = this.listener.parseURI( this,
this.req, this.rsp, this.inData, this.socket,
iAmFirst );
if ( servletURI == null ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestHandlerThread.KeepAliveTimedOut", this.threadName );
// Keep alive timed out - deallocate and go into wait state
this.listener.deallocateRequestResponse( this, req,
rsp, inData, outData );
continueFlag = false;
continue;
}
if ( this.simulateModUniqueId ) {
req.setAttribute( "UNIQUE_ID", "" + requestId );
}
long headerParseTime = getRequestProcessTime( );
iAmFirst = false;
HostConfiguration hostConfig = req.getHostGroup( ).getHostByName( req.getServerName( ) );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestHandlerThread.StartRequest",
new String[] {"" + requestId, hostConfig.getHostname( )} );
// Get the URI from the request, check for prefix, then
// match it to a requestDispatcher
WebAppConfiguration webAppConfig = hostConfig.getWebAppByURI( servletURI );
if ( webAppConfig == null ) {
webAppConfig = hostConfig.getWebAppByURI( "/" );
}
if ( webAppConfig == null ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"RequestHandlerThread.UnknownWebapp",
new String[] { servletURI } );
rsp.sendError( WinstoneResponse.SC_NOT_FOUND,
Launcher.RESOURCES.getString( "RequestHandlerThread.UnknownWebappPage", servletURI ) );
rsp.flushBuffer( );
req.discardRequestBody( );
writeToAccessLog( servletURI, req, rsp, null );
// Process keep-alive
continueFlag = this.listener.processKeepAlive( req, rsp, inSocket );
this.listener.deallocateRequestResponse( this, req, rsp, inData, outData );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "RequestHandlerThread.FinishRequest",
"" + requestId );
Logger.log( Logger.SPEED, Launcher.RESOURCES, "RequestHandlerThread.RequestTime",
new String[] { servletURI, "" + headerParseTime, "" + getRequestProcessTime( ) } );
continue;
}
req.setWebAppConfig( webAppConfig );
// Now we've verified it's in the right webapp, send
// request in scope notify
ServletRequestListener reqLsnrs[] = webAppConfig.getRequestListeners( );
for ( int n = 0; n < reqLsnrs.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( webAppConfig.getLoader( ) );
reqLsnrs[n].requestInitialized( new ServletRequestEvent( webAppConfig, req ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
// Lookup a dispatcher, then process with it
processRequest( webAppConfig, req, rsp,
webAppConfig.getServletURIFromRequestURI( servletURI ) );
writeToAccessLog( servletURI, req, rsp, webAppConfig );
this.outData.finishResponse( );
this.inData.finishRequest( );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestHandlerThread.FinishRequest",
"" + requestId );
// Process keep-alive
continueFlag = this.listener.processKeepAlive( req, rsp, inSocket );
// Set last accessed time on session as start of this
// request
req.markSessionsAsRequestFinished( this.requestStartTime, this.saveSessions );
// send request listener notifies
for ( int n = 0; n < reqLsnrs.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( webAppConfig.getLoader( ) );
reqLsnrs[n].requestDestroyed( new ServletRequestEvent( webAppConfig, req ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
req.setWebAppConfig( null );
rsp.setWebAppConfig( null );
req.setRequestAttributeListeners( null );
this.listener.deallocateRequestResponse( this, req, rsp, inData, outData );
Logger.log( Logger.SPEED, Launcher.RESOURCES, "RequestHandlerThread.RequestTime",
new String[] { servletURI, "" + headerParseTime,
"" + getRequestProcessTime( ) } );
} catch ( InterruptedIOException errIO ) {
continueFlag = false;
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestHandlerThread.SocketTimeout", errIO );
} catch ( SocketException errIO ) {
continueFlag = false;
}
}
this.listener.deallocateRequestResponse( this, req, rsp, inData, outData );
this.listener.releaseSocket( this.socket, inSocket, outSocket ); // shut sockets
} catch ( Throwable err ) {
try {
this.listener.deallocateRequestResponse( this, req, rsp, inData, outData );
} catch ( Throwable errClose ) {
}
try {
this.listener.releaseSocket( this.socket, inSocket,
outSocket ); // shut sockets
} catch ( Throwable errClose ) {
}
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"RequestHandlerThread.RequestError", err );
}
this.objectPool.releaseRequestHandler( this );
if ( !interrupted ) {
// Suspend this thread until we get assigned and woken up
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestHandlerThread.EnterWaitState" );
try {
synchronized ( this ) {
this.wait( );
}
} catch ( InterruptedException err ) {
interrupted = true;
}
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestHandlerThread.WakingUp" );
}
}
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "RequestHandlerThread.ThreadExit" );
}
/**
* Actually process the request. This takes the request and response, and feeds
* them to the desired servlet, which then processes them or throws them off to
* another servlet.
*/
233 private void processRequest( WebAppConfiguration webAppConfig, WinstoneRequest req,
234 WinstoneResponse rsp, String path ) throws IOException, ServletException {
RequestDispatcher rd = null;
javax.servlet.RequestDispatcher rdError = null;
try {
rd = webAppConfig.getInitialDispatcher( path, req, rsp );
// Null RD means an error or we have been redirected to a welcome page
if ( rd != null ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"RequestHandlerThread.HandlingRD", rd.getName( ) );
rd.forward( req, rsp );
}
// if null returned, assume we were redirected
} catch ( Throwable err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"RequestHandlerThread.UntrappedError", err );
rdError = webAppConfig.getErrorDispatcherByClass( err );
}
// If there was any kind of error, execute the error dispatcher here
if ( rdError != null ) {
try {
if ( rsp.isCommitted( ) ) {
rdError.include( req, rsp );
} else {
rsp.resetBuffer( );
rdError.forward( req, rsp );
}
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "RequestHandlerThread.ErrorInErrorServlet", err );
}
// rsp.sendUntrappedError( err, req, rd != null ? rd.getName( ) : null );
}
rsp.flushBuffer( );
rsp.getWinstoneOutputStream( ).setClosed( true );
req.discardRequestBody( );
}
/**
* Assign a socket to the handler
*/
275 public void commenceRequestHandling( Socket socket, Listener listener ) {
this.listener = listener;
this.socket = socket;
if ( this.thread.isAlive( ) )
synchronized ( this ) {
this.notifyAll( );
}
else
this.thread.start( );
}
286 public void setRequest( WinstoneRequest request ) {
this.req = request;
}
290 public void setResponse( WinstoneResponse response ) {
this.rsp = response;
}
294 public void setInStream( WinstoneInputStream inStream ) {
this.inData = inStream;
}
298 public void setOutStream( WinstoneOutputStream outStream ) {
this.outData = outStream;
}
302 public void setRequestStartTime( ) {
this.requestStartTime = System.currentTimeMillis( );
}
306 public long getRequestProcessTime( ) {
return System.currentTimeMillis( ) - this.requestStartTime;
}
/**
* Trigger the thread destruction for this handler
*/
313 public void destroy( ) {
if ( this.thread.isAlive( ) ) {
this.thread.interrupt( );
}
}
319 protected void writeToAccessLog( String originalURL, WinstoneRequest request, WinstoneResponse response,
320 WebAppConfiguration webAppConfig ) {
if ( webAppConfig != null ) {
// Log a row containing appropriate data
AccessLogger logger = webAppConfig.getAccessLogger( );
if ( logger != null ) {
logger.log( originalURL, request, response );
}
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
/**
* This is the one that keeps a specific servlet instance's config, as well as
* holding the instance itself.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ServletConfiguration.java, v 1.16 2007/04/23 02:55:35 rickknowles Exp $
*/
32 public class ServletConfiguration implements javax.servlet.ServletConfig,
Comparable {
35 static final String ELEM_NAME = "servlet-name";
36 static final String ELEM_DISPLAY_NAME = "display-name";
37 static final String ELEM_CLASS = "servlet-class";
38 static final String ELEM_JSP_FILE = "jsp-file";
39 static final String ELEM_DESCRIPTION = "description";
40 static final String ELEM_INIT_PARAM = "init-param";
41 static final String ELEM_INIT_PARAM_NAME = "param-name";
42 static final String ELEM_INIT_PARAM_VALUE = "param-value";
43 static final String ELEM_LOAD_ON_STARTUP = "load-on-startup";
44 static final String ELEM_RUN_AS = "run-as";
45 static final String ELEM_SECURITY_ROLE_REF = "security-role-ref";
46 static final String ELEM_ROLE_NAME = "role-name";
47 static final String ELEM_ROLE_LINK = "role-link";
49 final String JSP_FILE = "org.apache.catalina.jsp_file";
51 private String servletName;
52 private String classFile;
53 private Servlet instance;
54 private Map initParameters;
55 private WebAppConfiguration webAppConfig;
private int loadOnStartup;
57 private String jspFile;
// private String runAsRole;
59 private Map securityRoleRefs;
60 private Object servletSemaphore = new Boolean( true );
private boolean isSingleThreadModel = false;
private boolean unavailable = false;
63 private Throwable unavailableException = null;
65 protected ServletConfiguration( WebAppConfiguration webAppConfig ) {
this.webAppConfig = webAppConfig;
this.initParameters = new Hashtable( );
this.loadOnStartup = -1;
this.securityRoleRefs = new Hashtable( );
}
72 public ServletConfiguration( WebAppConfiguration webAppConfig, String servletName,
73 String className, Map initParams, int loadOnStartup ) {
this( webAppConfig );
if ( initParams != null )
this.initParameters.putAll( initParams );
this.servletName = servletName;
this.classFile = className;
this.jspFile = null;
this.loadOnStartup = loadOnStartup;
}
83 public ServletConfiguration( WebAppConfiguration webAppConfig, Node elm ) {
this( webAppConfig );
// Parse the web.xml file entry
for ( int n = 0; n < elm.getChildNodes( ).getLength( ); n++ ) {
Node child = elm.getChildNodes( ).item( n );
if ( child.getNodeType( ) != Node.ELEMENT_NODE )
continue;
String nodeName = child.getNodeName( );
// Construct the servlet instances
if ( nodeName.equals( ELEM_NAME ) )
this.servletName = WebAppConfiguration.getTextFromNode( child );
else if ( nodeName.equals( ELEM_CLASS ) )
this.classFile = WebAppConfiguration.getTextFromNode( child );
else if ( nodeName.equals( ELEM_JSP_FILE ) )
this.jspFile = WebAppConfiguration.getTextFromNode( child );
else if ( nodeName.equals( ELEM_LOAD_ON_STARTUP ) ) {
String index = child.getFirstChild( ) == null ? "-1" :
WebAppConfiguration.getTextFromNode( child );
this.loadOnStartup = Integer.parseInt( index );
} else if ( nodeName.equals( ELEM_INIT_PARAM ) ) {
String paramName = "";
String paramValue = "";
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node paramNode = child.getChildNodes( ).item( k );
if ( paramNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( paramNode.getNodeName( ).equals( ELEM_INIT_PARAM_NAME ) )
paramName = WebAppConfiguration.getTextFromNode( paramNode );
else if ( paramNode.getNodeName( ).equals( ELEM_INIT_PARAM_VALUE ) )
paramValue = WebAppConfiguration.getTextFromNode( paramNode );
}
if ( !paramName.equals( "" ) ) {
this.initParameters.put( paramName, paramValue );
}
} else if ( nodeName.equals( ELEM_RUN_AS ) ) {
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node roleElm = child.getChildNodes( ).item( m );
if ( ( roleElm.getNodeType( ) == Node.ELEMENT_NODE )
&& ( roleElm.getNodeName( ).equals( ELEM_ROLE_NAME ) ) ) {
// this.runAsRole = WebAppConfiguration.getTextFromNode( roleElm ); // not used
}
}
} else if ( nodeName.equals( ELEM_SECURITY_ROLE_REF ) ) {
String name = "";
String link = "";
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node roleRefNode = child.getChildNodes( ).item( k );
if ( roleRefNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( roleRefNode.getNodeName( ).equals( ELEM_ROLE_NAME ) )
name = WebAppConfiguration.getTextFromNode( roleRefNode );
else if ( roleRefNode.getNodeName( ).equals( ELEM_ROLE_LINK ) )
link = WebAppConfiguration.getTextFromNode( roleRefNode );
}
if ( !name.equals( "" ) && !link.equals( "" ) )
this.initParameters.put( name, link );
}
}
if ( ( this.jspFile != null ) && ( this.classFile == null ) ) {
this.classFile = WebAppConfiguration.JSP_SERVLET_CLASS;
WebAppConfiguration.addJspServletParams( this.initParameters );
}
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"ServletConfiguration.DeployedInstance", new String[] {
this.servletName, this.classFile } );
}
153 public void ensureInitialization( ) {
if ( this.instance != null ) {
return; // already init'd
}
synchronized ( this.servletSemaphore ) {
if ( this.instance != null ) {
return; // already init'd
}
// Check if we were decommissioned while blocking
if ( this.unavailableException != null ) {
return;
}
// If no instance, class load, then call init( )
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
Servlet newInstance = null;
Throwable otherError = null;
try {
Class servletClass = Class.forName( classFile, true, this.webAppConfig.getLoader( ) );
newInstance = ( Servlet ) servletClass.newInstance( );
this.isSingleThreadModel = Class.forName( "javax.servlet.SingleThreadModel" ).isInstance( newInstance );
// Initialise with the correct classloader
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "ServletConfiguration.init", this.servletName );
newInstance.init( this );
this.instance = newInstance;
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.ClassLoadError", this.classFile, err );
setUnavailable( newInstance );
this.unavailableException = err;
} catch ( IllegalAccessException err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.ClassLoadError", this.classFile, err );
setUnavailable( newInstance );
this.unavailableException = err;
} catch ( InstantiationException err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.ClassLoadError", this.classFile, err );
setUnavailable( newInstance );
this.unavailableException = err;
} catch ( ServletException err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"ServletConfiguration.InitError", this.servletName, err );
this.instance = null; // so that we don't call the destroy method
setUnavailable( newInstance );
this.unavailableException = err;
} catch ( RuntimeException err ) {
otherError = err;
throw err;
} catch ( Error err ) {
otherError = err;
throw err;
} finally {
Thread.currentThread( ).setContextClassLoader( cl );
if ( ( otherError == null ) && ( this.unavailableException == null ) ) {
this.instance = newInstance;
}
}
}
return;
}
222 public void execute( ServletRequest request, ServletResponse response, String requestURI )
throws ServletException, IOException {
ensureInitialization( );
// If init failed, return 500 error
if ( this.unavailable ) {
// ( ( HttpServletResponse ) response ).sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
// resources.getString( "StaticResourceServlet.PathNotFound", requestURI ) );
RequestDispatcher rd = this.webAppConfig.getErrorDispatcherByClass(
this.unavailableException );
rd.forward( request, response );
return;
}
if ( this.jspFile != null )
request.setAttribute( JSP_FILE, this.jspFile );
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
try {
if ( this.isSingleThreadModel ) {
synchronized ( this ) {
this.instance.service( request, response );
}
} else
this.instance.service( request, response );
} catch ( UnavailableException err ) {
// catch locally and rethrow as a new ServletException, so
// we only invalidate the throwing servlet
setUnavailable( this.instance );
( ( HttpServletResponse ) response ).sendError( HttpServletResponse.SC_NOT_FOUND,
Launcher.RESOURCES.getString( "StaticResourceServlet.PathNotFound", requestURI ) );
// throw new ServletException( resources.getString(
// "RequestDispatcher.ForwardError" ), err );
} finally {
Thread.currentThread( ).setContextClassLoader( cl );
}
}
263 public int getLoadOnStartup( ) {
return this.loadOnStartup;
}
267 public String getInitParameter( String name ) {
return ( String ) this.initParameters.get( name );
}
271 public Enumeration getInitParameterNames( ) {
return Collections.enumeration( this.initParameters.keySet( ) );
}
275 public ServletContext getServletContext( ) {
return this.webAppConfig;
}
279 public String getServletName( ) {
return this.servletName;
}
283 public Map getSecurityRoleRefs( ) {
return this.securityRoleRefs;
}
/**
* This was included so that the servlet instances could be sorted on their
* loadOnStartup values. Otherwise used.
*/
291 public int compareTo( Object objTwo ) {
Integer one = new Integer( this.loadOnStartup );
Integer two = new Integer( ( ( ServletConfiguration ) objTwo ).loadOnStartup );
return one.compareTo( two );
}
/**
* Called when it's time for the container to shut this servlet down.
*/
300 public void destroy( ) {
synchronized ( this.servletSemaphore ) {
setUnavailable( this.instance );
}
}
306 protected void setUnavailable( Servlet unavailableServlet ) {
this.unavailable = true;
if ( unavailableServlet != null ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"ServletConfiguration.destroy", this.servletName );
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
try {
unavailableServlet.destroy( );
} finally {
Thread.currentThread( ).setContextClassLoader( cl );
this.instance = null;
}
}
// remove from webapp
this.webAppConfig.removeServletConfigurationAndMappings( this );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
/**
* A jvm hook to force the calling of the web-app destroy before the process terminates
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ShutdownHook.java, v 1.3 2006/02/28 07:32:47 rickknowles Exp $
*/
15 public class ShutdownHook extends Thread {
16 private Launcher launcher;
18 public ShutdownHook( Launcher launcher ) {
this.launcher = launcher;
}
22 public void run( ) {
if ( this.launcher != null )
this.launcher.shutdown( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet to handle static resources. Simply finds and sends them, or
* dispatches to the error servlet.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: StaticResourceServlet.java, v 1.17 2004/12/31 07:21:00
* rickknowles Exp $
*/
39 public class StaticResourceServlet extends HttpServlet {
// final String JSP_FILE = "org.apache.catalina.jsp_file";
41 final static String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
42 final static String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
43 final static String CACHED_RESOURCE_DATE_HEADER = "If-Modified-Since";
44 final static String LAST_MODIFIED_DATE_HEADER = "Last-Modified";
45 final static String RANGE_HEADER = "Range";
46 final static String ACCEPT_RANGES_HEADER = "Accept-Ranges";
47 final static String CONTENT_RANGE_HEADER = "Content-Range";
48 final static String RESOURCE_FILE = "winstone.LocalStrings";
49 private DateFormat sdfFileDate = new SimpleDateFormat( "dd-MM-yyyy HH:mm" );
50 private File webRoot;
51 private String prefix;
private boolean directoryList;
54 public void init( ServletConfig config ) throws ServletException {
super.init( config );
this.webRoot = new File( config.getInitParameter( "webRoot" ) );
this.prefix = config.getInitParameter( "prefix" );
String dirList = config.getInitParameter( "directoryList" );
this.directoryList = ( dirList == null )
|| dirList.equalsIgnoreCase( "true" )
|| dirList.equalsIgnoreCase( "yes" );
}
64 public void doPost( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
doGet( request, response );
}
69 public void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
boolean isInclude = ( request.getAttribute( INCLUDE_SERVLET_PATH ) != null );
boolean isForward = ( request.getAttribute( FORWARD_SERVLET_PATH ) != null );
String path = null;
if ( isInclude )
path = ( String ) request.getAttribute( INCLUDE_SERVLET_PATH );
else {
path = request.getServletPath( );
}
// URL decode path
path = WinstoneRequest.decodeURLToken( path );
long cachedResDate = request.getDateHeader( CACHED_RESOURCE_DATE_HEADER );
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"StaticResourceServlet.PathRequested", new String[] {
getServletConfig( ).getServletName( ), path } );
// Check for the resource
File res = path.equals( "" ) ? this.webRoot : new File(
this.webRoot, path );
// Send a 404 if not found
if ( !res.exists( ) )
response.sendError( HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
.getString( "StaticResourceServlet.PathNotFound", path ) );
// Check we are below the webroot
else if ( !isDescendant( this.webRoot, res, this.webRoot ) ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "StaticResourceServlet.OutsideWebroot",
new String[] {res.getCanonicalPath( ), this.webRoot.toString( )} );
response.sendError( HttpServletResponse.SC_FORBIDDEN, Launcher.RESOURCES
.getString( "StaticResourceServlet.PathInvalid", path ) );
}
// Check we are not below the web-inf
else if ( !isInclude && !isForward && isDescendant( new File( this.webRoot, "WEB-INF" ), res, this.webRoot ) )
response.sendError( HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
.getString( "StaticResourceServlet.PathInvalid", path ) );
// Check we are not below the meta-inf
else if ( !isInclude && !isForward && isDescendant( new File( this.webRoot, "META-INF" ), res, this.webRoot ) )
response.sendError( HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
.getString( "StaticResourceServlet.PathInvalid", path ) );
// check for the directory case
else if ( res.isDirectory( ) ) {
if ( path.endsWith( "/" ) ) {
// Try to match each of the welcome files
// String matchedWelcome = matchWelcomeFiles( path, res );
// if ( matchedWelcome != null )
// response.sendRedirect( this.prefix + path + matchedWelcome );
// else
if ( this.directoryList )
generateDirectoryList( request, response, path );
else
response.sendError( HttpServletResponse.SC_FORBIDDEN,
Launcher.RESOURCES.getString( "StaticResourceServlet.AccessDenied" ) );
} else
response.sendRedirect( this.prefix + path + "/" );
}
// Send a 304 if not modified
else if ( !isInclude && ( cachedResDate != -1 )
&& ( cachedResDate < ( System.currentTimeMillis( ) / 1000L * 1000L ) )
&& ( cachedResDate >= ( res.lastModified( ) / 1000L * 1000L ) ) ) {
String mimeType = getServletContext( ).getMimeType(
res.getName( ).toLowerCase( ) );
if ( mimeType != null )
response.setContentType( mimeType );
response.setStatus( HttpServletResponse.SC_NOT_MODIFIED );
response.setContentLength( 0 );
response.flushBuffer( );
}
// Write out the resource if not range or is included
else if ( ( request.getHeader( RANGE_HEADER ) == null ) || isInclude ) {
String mimeType = getServletContext( ).getMimeType(
res.getName( ).toLowerCase( ) );
if ( mimeType != null )
response.setContentType( mimeType );
InputStream resStream = new FileInputStream( res );
response.setStatus( HttpServletResponse.SC_OK );
response.setContentLength( ( int ) res.length( ) );
// response.addHeader( ACCEPT_RANGES_HEADER, "bytes" );
response.addDateHeader( LAST_MODIFIED_DATE_HEADER, res.lastModified( ) );
OutputStream out = null;
Writer outWriter = null;
try {
out = response.getOutputStream( );
} catch ( IllegalStateException err ) {
outWriter = response.getWriter( );
} catch ( IllegalArgumentException err ) {
outWriter = response.getWriter( );
}
byte buffer[] = new byte[4096];
int read = resStream.read( buffer );
while ( read > 0 ) {
if ( out != null ) {
out.write( buffer, 0, read );
} else {
outWriter.write( new String( buffer, 0, read,
response.getCharacterEncoding( ) ) );
}
read = resStream.read( buffer );
}
resStream.close( );
} else if ( request.getHeader( RANGE_HEADER ).startsWith( "bytes=" ) ) {
String mimeType = getServletContext( ).getMimeType(
res.getName( ).toLowerCase( ) );
if ( mimeType != null )
response.setContentType( mimeType );
InputStream resStream = new FileInputStream( res );
List ranges = new ArrayList( );
StringTokenizer st = new StringTokenizer( request.getHeader(
RANGE_HEADER ).substring( 6 ).trim( ), ", ", false );
int totalSent = 0;
String rangeText = "";
while ( st.hasMoreTokens( ) ) {
String rangeBlock = st.nextToken( );
int start = 0;
int end = ( int ) res.length( );
int delim = rangeBlock.indexOf( '-' );
if ( delim != 0 )
start = Integer.parseInt( rangeBlock.substring( 0, delim )
.trim( ) );
if ( delim != rangeBlock.length( ) - 1 )
end = Integer.parseInt( rangeBlock.substring( delim + 1 )
.trim( ) );
totalSent += ( end - start );
rangeText += ", " + start + "-" + end;
ranges.add( start + "-" + end );
}
response.setStatus( HttpServletResponse.SC_PARTIAL_CONTENT );
response.addHeader( CONTENT_RANGE_HEADER, "bytes "
+ rangeText.substring( 1 ) + "/" + res.length( ) );
response.setContentLength( totalSent );
response.addHeader( ACCEPT_RANGES_HEADER, "bytes" );
response.addDateHeader( LAST_MODIFIED_DATE_HEADER, res
.lastModified( ) );
OutputStream out = response.getOutputStream( );
int bytesRead = 0;
for ( Iterator i = ranges.iterator( ); i.hasNext( ); ) {
String rangeBlock = ( String ) i.next( );
int delim = rangeBlock.indexOf( '-' );
int start = Integer.parseInt( rangeBlock.substring( 0, delim ) );
int end = Integer.parseInt( rangeBlock.substring( delim + 1 ) );
int read = 0;
while ( ( read != -1 ) && ( bytesRead <= res.length( ) ) ) {
read = resStream.read( );
if ( ( bytesRead >= start ) && ( bytesRead < end ) )
out.write( read );
bytesRead++;
}
}
resStream.close( );
} else
response
.sendError( HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE );
}
/**
* Generate a list of the files in this directory
*/
238 private void generateDirectoryList( HttpServletRequest request,
239 HttpServletResponse response, String path ) throws ServletException,
IOException {
// Get the file list
File dir = path.equals( "" ) ? this.webRoot : new File(
this.webRoot, path );
File children[] = dir.listFiles( );
Arrays.sort( children );
// Build row content
StringWriter rowString = new StringWriter( );
String oddColour = Launcher.RESOURCES
.getString( "StaticResourceServlet.DirectoryList.OddColour" );
String evenColour = Launcher.RESOURCES
.getString( "StaticResourceServlet.DirectoryList.EvenColour" );
String rowTextColour = Launcher.RESOURCES
.getString( "StaticResourceServlet.DirectoryList.RowTextColour" );
String directoryLabel = Launcher.RESOURCES
.getString( "StaticResourceServlet.DirectoryList.DirectoryLabel" );
String parentDirLabel = Launcher.RESOURCES
.getString( "StaticResourceServlet.DirectoryList.ParentDirectoryLabel" );
String noDateLabel = Launcher.RESOURCES
.getString( "StaticResourceServlet.DirectoryList.NoDateLabel" );
int rowCount = 0;
// Write the parent dir row
if ( !path.equals( "" ) && !path.equals( "/" ) ) {
rowString.write( Launcher.RESOURCES.getString(
"StaticResourceServlet.DirectoryList.Row", new String[] {
rowTextColour, evenColour, parentDirLabel, "..",
noDateLabel, directoryLabel } ) );
rowCount++;
}
// Write the rows for each file
for ( int n = 0; n < children.length; n++ ) {
if ( !children[n].getName( ).equalsIgnoreCase( "web-inf" ) &&
!children[n].getName( ).equalsIgnoreCase( "meta-inf" ) ) {
File file = children[n];
String date = noDateLabel;
String size = directoryLabel;
if ( !file.isDirectory( ) ) {
size = "" + file.length( );
synchronized ( sdfFileDate ) {
date = sdfFileDate.format( new Date( file.lastModified( ) ) );
}
}
rowString.write( Launcher.RESOURCES.getString(
"StaticResourceServlet.DirectoryList.Row",
new String[] {
rowTextColour,
rowCount % 2 == 0 ? evenColour : oddColour,
file.getName( ) + ( file.isDirectory( ) ? "/" : "" ),
"./" + file.getName( ) + ( file.isDirectory( ) ? "/" : "" ),
date, size} ) );
rowCount++;
}
}
// Build wrapper body
String out = Launcher.RESOURCES.getString( "StaticResourceServlet.DirectoryList.Body",
new String[] {
Launcher.RESOURCES.getString( "StaticResourceServlet.DirectoryList.HeaderColour" ),
Launcher.RESOURCES.getString( "StaticResourceServlet.DirectoryList.HeaderTextColour" ),
Launcher.RESOURCES.getString( "StaticResourceServlet.DirectoryList.LabelColour" ),
Launcher.RESOURCES.getString( "StaticResourceServlet.DirectoryList.LabelTextColour" ),
new Date( ) + "",
Launcher.RESOURCES.getString( "ServerVersion" ),
path.equals( "" ) ? "/" : path,
rowString.toString( ) } );
response.setContentLength( out.getBytes( ).length );
response.setContentType( "text/html" );
Writer w = response.getWriter( );
w.write( out );
w.close( );
}
318 public static boolean isDescendant( File parent, File child, File commonBase ) throws IOException {
if ( child.equals( parent ) ) {
return true;
} else {
// Start by checking canonicals
String canonicalParent = parent.getAbsoluteFile( ).getCanonicalPath( );
String canonicalChild = child.getAbsoluteFile( ).getCanonicalPath( );
if ( canonicalChild.startsWith( canonicalParent ) ) {
return true;
}
// If canonicals don't match, we're dealing with symlinked files, so if we can
// build a path from the parent to the child,
String childOCValue = constructOurCanonicalVersion( child, commonBase );
String parentOCValue = constructOurCanonicalVersion( parent, commonBase );
return childOCValue.startsWith( parentOCValue );
}
}
337 public static String constructOurCanonicalVersion( File current, File stopPoint ) {
int backOnes = 0;
StringBuffer ourCanonicalVersion = new StringBuffer( );
while ( ( current != null ) && !current.equals( stopPoint ) ) {
if ( current.getName( ).equals( ".." ) ) {
backOnes++;
} else if ( current.getName( ).equals( "." ) ) {
// skip - do nothing
} else if ( backOnes > 0 ) {
backOnes--;
} else {
ourCanonicalVersion.insert( 0, "/" + current.getName( ) );
}
current = current.getParentFile( );
}
return ourCanonicalVersion.toString( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
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.ServletRequestAttributeListener;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionListener;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Models the web.xml file's details ... basically just a bunch of configuration
* details, plus the actual instances of mounted servlets.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WebAppConfiguration.java, v 1.55 2007/11/13 01:42:47 rickknowles Exp $
*/
56 public class WebAppConfiguration implements ServletContext, Comparator {
// private static final String ELEM_DESCRIPTION = "description";
58 private static final String ELEM_DISPLAY_NAME = "display-name";
59 private static final String ELEM_SERVLET = "servlet";
60 private static final String ELEM_SERVLET_MAPPING = "servlet-mapping";
61 private static final String ELEM_SERVLET_NAME = "servlet-name";
62 private static final String ELEM_FILTER = "filter";
63 private static final String ELEM_FILTER_MAPPING = "filter-mapping";
64 private static final String ELEM_FILTER_NAME = "filter-name";
65 private static final String ELEM_DISPATCHER = "dispatcher";
66 private static final String ELEM_URL_PATTERN = "url-pattern";
67 private static final String ELEM_WELCOME_FILES = "welcome-file-list";
68 private static final String ELEM_WELCOME_FILE = "welcome-file";
69 private static final String ELEM_SESSION_CONFIG = "session-config";
70 private static final String ELEM_SESSION_TIMEOUT = "session-timeout";
71 private static final String ELEM_MIME_MAPPING = "mime-mapping";
72 private static final String ELEM_MIME_EXTENSION = "extension";
73 private static final String ELEM_MIME_TYPE = "mime-type";
74 private static final String ELEM_CONTEXT_PARAM = "context-param";
75 private static final String ELEM_PARAM_NAME = "param-name";
76 private static final String ELEM_PARAM_VALUE = "param-value";
77 private static final String ELEM_LISTENER = "listener";
78 private static final String ELEM_LISTENER_CLASS = "listener-class";
79 private static final String ELEM_DISTRIBUTABLE = "distributable";
80 private static final String ELEM_ERROR_PAGE = "error-page";
81 private static final String ELEM_EXCEPTION_TYPE = "exception-type";
82 private static final String ELEM_ERROR_CODE = "error-code";
83 private static final String ELEM_ERROR_LOCATION = "location";
84 private static final String ELEM_SECURITY_CONSTRAINT = "security-constraint";
85 private static final String ELEM_LOGIN_CONFIG = "login-config";
86 private static final String ELEM_SECURITY_ROLE = "security-role";
87 private static final String ELEM_ROLE_NAME = "role-name";
88 private static final String ELEM_ENV_ENTRY = "env-entry";
89 private static final String ELEM_LOCALE_ENC_MAP_LIST = "locale-encoding-mapping-list";
90 private static final String ELEM_LOCALE_ENC_MAPPING = "locale-encoding-mapping";
91 private static final String ELEM_LOCALE = "locale";
92 private static final String ELEM_ENCODING = "encoding";
93 private static final String ELEM_JSP_CONFIG = "jsp-config";
94 private static final String ELEM_JSP_PROPERTY_GROUP = "jsp-property-group";
96 private static final String DISPATCHER_REQUEST = "REQUEST";
97 private static final String DISPATCHER_FORWARD = "FORWARD";
98 private static final String DISPATCHER_INCLUDE = "INCLUDE";
99 private static final String DISPATCHER_ERROR = "ERROR";
100 private static final String JSP_SERVLET_NAME = "JspServlet";
101 private static final String JSP_SERVLET_MAPPING = "*.jsp";
102 private static final String JSPX_SERVLET_MAPPING = "*.jspx";
103 private static final String JSP_SERVLET_LOG_LEVEL = "WARNING";
104 private static final String INVOKER_SERVLET_NAME = "invoker";
105 private static final String INVOKER_SERVLET_CLASS = "winstone.invoker.InvokerServlet";
106 private static final String DEFAULT_INVOKER_PREFIX = "/servlet/";
107 private static final String DEFAULT_SERVLET_NAME = "default";
108 private static final String DEFAULT_SERVLET_CLASS = "winstone.StaticResourceServlet";
109 private static final String DEFAULT_REALM_CLASS = "winstone.realm.ArgumentsRealm";
110 private static final String DEFAULT_JNDI_MGR_CLASS = "winstone.jndi.WebAppJNDIManager";
111 private static final String RELOADING_CL_CLASS = "winstone.classLoader.ReloadingClassLoader";
112 private static final String WEBAPP_CL_CLASS = "winstone.classLoader.WebappClassLoader";
113 private static final String ERROR_SERVLET_NAME = "winstoneErrorServlet";
114 private static final String ERROR_SERVLET_CLASS = "winstone.ErrorServlet";
116 private static final String WEB_INF = "WEB-INF";
117 private static final String CLASSES = "classes/";
118 private static final String LIB = "lib";
120 static final String JSP_SERVLET_CLASS = "org.apache.jasper.servlet.JspServlet";
122 private HostConfiguration ownerHostConfig;
123 private Cluster cluster;
124 private String webRoot;
125 private String prefix;
126 private String contextName;
127 private ClassLoader loader;
128 private String displayName;
129 private Map attributes;
130 private Map initParameters;
131 private Map sessions;
132 private Map mimeTypes;
133 private Map servletInstances;
134 private Map filterInstances;
135 private ServletContextAttributeListener contextAttributeListeners[];
136 private ServletContextListener contextListeners[];
137 private ServletRequestListener requestListeners[];
138 private ServletRequestAttributeListener requestAttributeListeners[];
139 private HttpSessionActivationListener sessionActivationListeners[];
140 private HttpSessionAttributeListener sessionAttributeListeners[];
141 private HttpSessionListener sessionListeners[];
142 private Throwable contextStartupError;
143 private Map exactServletMatchMounts;
144 private Mapping patternMatches[];
145 private Mapping filterPatternsRequest[];
146 private Mapping filterPatternsForward[];
147 private Mapping filterPatternsInclude[];
148 private Mapping filterPatternsError[];
149 private AuthenticationHandler authenticationHandler;
150 private AuthenticationRealm authenticationRealm;
151 private String welcomeFiles[];
152 private Integer sessionTimeout;
153 private Class[] errorPagesByExceptionKeysSorted;
154 private Map errorPagesByException;
155 private Map errorPagesByCode;
156 private Map localeEncodingMap;
157 private String defaultServletName;
158 private String errorServletName;
159 private JNDIManager jndiManager;
160 private AccessLogger accessLogger;
161 private Map filterMatchCache;
private boolean useSavedSessions;
164 public static boolean booleanArg( Map args, String name, boolean defaultTrue ) {
String value = ( String ) args.get( name );
if ( defaultTrue )
return ( value == null ) || ( value.equalsIgnoreCase( "true" ) || value.equalsIgnoreCase( "yes" ) );
else
return ( value != null ) && ( value.equalsIgnoreCase( "true" ) || value.equalsIgnoreCase( "yes" ) );
}
172 public static String stringArg( Map args, String name, String defaultValue ) {
return ( String ) ( args.get( name ) == null ? defaultValue : args.get( name ) );
}
176 public static int intArg( Map args, String name, int defaultValue ) {
return Integer.parseInt( stringArg( args, name, "" + defaultValue ) );
}
180 public static String getTextFromNode( Node node ) {
if ( node == null ) {
return null;
}
Node child = node.getFirstChild( );
if ( child == null ) {
return "";
}
String textNode = child.getNodeValue( );
if ( textNode == null ) {
return "";
} else {
return textNode.trim( );
}
}
196 public static boolean useSavedSessions( Map args ) {
return booleanArg( args, "useSavedSessions", false );
}
/**
* Constructor. This parses the xml and sets up for basic routing
*/
203 public WebAppConfiguration( HostConfiguration ownerHostConfig, Cluster cluster, String webRoot,
204 String prefix, ObjectPool objectPool, Map startupArgs, Node elm,
205 ClassLoader parentClassLoader, File parentClassPaths[], String contextName ) {
if ( !prefix.equals( "" ) && !prefix.startsWith( "/" ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.AddingLeadingSlash", prefix );
prefix = "/" + prefix;
}
this.ownerHostConfig = ownerHostConfig;
this.webRoot = webRoot;
this.prefix = prefix;
this.contextName = contextName;
List localLoaderClassPathFiles = new ArrayList( );
this.loader = buildWebAppClassLoader( startupArgs, parentClassLoader,
webRoot, localLoaderClassPathFiles );
// Build switch values
boolean useJasper = booleanArg( startupArgs, "useJasper", true );
boolean useInvoker = booleanArg( startupArgs, "useInvoker", false );
boolean useJNDI = booleanArg( startupArgs, "useJNDI", false );
this.useSavedSessions = useSavedSessions( startupArgs );
// Check jasper is available - simple tests
if ( useJasper ) {
try {
Class.forName( "javax.servlet.jsp.JspFactory", true, parentClassLoader );
Class.forName( JSP_SERVLET_CLASS, true, this.loader );
} catch ( Throwable err ) {
if ( booleanArg( startupArgs, "useJasper", false ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.JasperNotFound" );
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.JasperLoadException", err );
}
useJasper = false;
}
}
if ( useInvoker ) {
try {
Class.forName( INVOKER_SERVLET_CLASS, false, this.loader );
} catch ( Throwable err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.InvokerNotFound" );
useInvoker = false;
}
}
this.attributes = new Hashtable( );
this.initParameters = new HashMap( );
this.sessions = new Hashtable( );
this.servletInstances = new HashMap( );
this.filterInstances = new HashMap( );
this.filterMatchCache = new HashMap( );
List contextAttributeListeners = new ArrayList( );
List contextListeners = new ArrayList( );
List requestListeners = new ArrayList( );
List requestAttributeListeners = new ArrayList( );
List sessionActivationListeners = new ArrayList( );
List sessionAttributeListeners = new ArrayList( );
List sessionListeners = new ArrayList( );
this.errorPagesByException = new HashMap( );
this.errorPagesByCode = new HashMap( );
boolean distributable = false;
this.exactServletMatchMounts = new Hashtable( );
List localFolderPatterns = new ArrayList( );
List localExtensionPatterns = new ArrayList( );
List lfpRequest = new ArrayList( );
List lfpForward = new ArrayList( );
List lfpInclude = new ArrayList( );
List lfpError = new ArrayList( );
List localWelcomeFiles = new ArrayList( );
List startupServlets = new ArrayList( );
Set rolesAllowed = new HashSet( );
List constraintNodes = new ArrayList( );
List envEntryNodes = new ArrayList( );
List localErrorPagesByExceptionList = new ArrayList( );
Node loginConfigNode = null;
// Add the class loader as an implicit context listener if it implements the interface
addListenerInstance( this.loader, contextAttributeListeners,
contextListeners, requestAttributeListeners, requestListeners,
sessionActivationListeners, sessionAttributeListeners,
sessionListeners );
// init mimeTypes set
this.mimeTypes = new Hashtable( );
String allTypes = Launcher.RESOURCES.getString( "WebAppConfig.DefaultMimeTypes" );
StringTokenizer mappingST = new StringTokenizer( allTypes, ":", false );
for ( ; mappingST.hasMoreTokens( ); ) {
String mapping = mappingST.nextToken( );
int delimPos = mapping.indexOf( '=' );
if ( delimPos == -1 )
continue;
String extension = mapping.substring( 0, delimPos );
String mimeType = mapping.substring( delimPos + 1 );
this.mimeTypes.put( extension.toLowerCase( ), mimeType );
}
this.localeEncodingMap = new HashMap( );
String encodingMapSet = Launcher.RESOURCES.getString( "WebAppConfig.EncodingMap" );
StringTokenizer st = new StringTokenizer( encodingMapSet, ";" );
for ( ; st.hasMoreTokens( ); ) {
String token = st.nextToken( );
int delimPos = token.indexOf( "=" );
if ( delimPos == -1 )
continue;
this.localeEncodingMap.put( token.substring( 0, delimPos ), token
.substring( delimPos + 1 ) );
}
// init jsp mappings set
List jspMappings = new ArrayList( );
jspMappings.add( JSP_SERVLET_MAPPING );
jspMappings.add( JSPX_SERVLET_MAPPING );
// Add required context atttributes
File tmpDir = new File( new File( new File( System.getProperty( "java.io.tmpdir" ),
"winstone.tmp" ), ownerHostConfig.getHostname( ) ), contextName );
tmpDir.mkdirs( );
this.attributes.put( "javax.servlet.context.tempdir", tmpDir );
// Parse the web.xml file
if ( elm != null ) {
NodeList children = elm.getChildNodes( );
for ( int n = 0; n < children.getLength( ); n++ ) {
Node child = children.item( n );
if ( child.getNodeType( ) != Node.ELEMENT_NODE )
continue;
String nodeName = child.getNodeName( );
if ( nodeName.equals( ELEM_DISPLAY_NAME ) )
this.displayName = getTextFromNode( child );
else if ( nodeName.equals( ELEM_DISTRIBUTABLE ) )
distributable = true;
else if ( nodeName.equals( ELEM_SECURITY_CONSTRAINT ) )
constraintNodes.add( child );
else if ( nodeName.equals( ELEM_ENV_ENTRY ) )
envEntryNodes.add( child );
else if ( nodeName.equals( ELEM_LOGIN_CONFIG ) )
loginConfigNode = child;
// Session config elements
else if ( nodeName.equals( ELEM_SESSION_CONFIG ) ) {
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node timeoutElm = child.getChildNodes( ).item( m );
if ( ( timeoutElm.getNodeType( ) == Node.ELEMENT_NODE )
&& ( timeoutElm.getNodeName( ).equals( ELEM_SESSION_TIMEOUT ) ) ) {
String timeoutStr = getTextFromNode( timeoutElm );
if ( !timeoutStr.equals( "" ) ) {
this.sessionTimeout = Integer.valueOf( timeoutStr );
}
}
}
}
// Construct the security roles
else if ( child.getNodeName( ).equals( ELEM_SECURITY_ROLE ) ) {
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node roleElm = child.getChildNodes( ).item( m );
if ( ( roleElm.getNodeType( ) == Node.ELEMENT_NODE )
&& ( roleElm.getNodeName( )
.equals( ELEM_ROLE_NAME ) ) )
rolesAllowed.add( getTextFromNode( roleElm ) );
}
}
// Construct the servlet instances
else if ( nodeName.equals( ELEM_SERVLET ) ) {
ServletConfiguration instance = new ServletConfiguration(
this, child );
this.servletInstances.put( instance.getServletName( ),
instance );
if ( instance.getLoadOnStartup( ) >= 0 )
startupServlets.add( instance );
}
// Construct the servlet instances
else if ( nodeName.equals( ELEM_FILTER ) ) {
FilterConfiguration instance = new FilterConfiguration(
this, this.loader, child );
this.filterInstances.put( instance.getFilterName( ), instance );
}
// Construct the servlet instances
else if ( nodeName.equals( ELEM_LISTENER ) ) {
String listenerClass = null;
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node listenerElm = child.getChildNodes( ).item( m );
if ( ( listenerElm.getNodeType( ) == Node.ELEMENT_NODE )
&& ( listenerElm.getNodeName( )
.equals( ELEM_LISTENER_CLASS ) ) )
listenerClass = getTextFromNode( listenerElm );
}
if ( listenerClass != null )
try {
Class listener = Class.forName( listenerClass, true,
this.loader );
Object listenerInstance = listener.newInstance( );
addListenerInstance( listenerInstance, contextAttributeListeners,
contextListeners, requestAttributeListeners, requestListeners,
sessionActivationListeners, sessionAttributeListeners,
sessionListeners );
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.AddListener", listenerClass );
} catch ( Throwable err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.InvalidListener",
listenerClass );
}
}
// Process the servlet mappings
else if ( nodeName.equals( ELEM_SERVLET_MAPPING ) ) {
String name = null;
List mappings = new ArrayList( );
// Parse the element and extract
NodeList mappingChildren = child.getChildNodes( );
for ( int k = 0; k < mappingChildren.getLength( ); k++ ) {
Node mapChild = mappingChildren.item( k );
if ( mapChild.getNodeType( ) != Node.ELEMENT_NODE )
continue;
String mapNodeName = mapChild.getNodeName( );
if ( mapNodeName.equals( ELEM_SERVLET_NAME ) ) {
name = getTextFromNode( mapChild );
} else if ( mapNodeName.equals( ELEM_URL_PATTERN ) ) {
mappings.add( getTextFromNode( mapChild ) );
}
}
for ( Iterator i = mappings.iterator( ); i.hasNext( ); ) {
processMapping( name, ( String ) i.next( ), this.exactServletMatchMounts,
localFolderPatterns, localExtensionPatterns );
}
}
// Process the filter mappings
else if ( nodeName.equals( ELEM_FILTER_MAPPING ) ) {
String filterName = null;
List mappings = new ArrayList( );
boolean onRequest = false;
boolean onForward = false;
boolean onInclude = false;
boolean onError = false;
// Parse the element and extract
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node mapChild = child.getChildNodes( ).item( k );
if ( mapChild.getNodeType( ) != Node.ELEMENT_NODE )
continue;
String mapNodeName = mapChild.getNodeName( );
if ( mapNodeName.equals( ELEM_FILTER_NAME ) ) {
filterName = getTextFromNode( mapChild );
} else if ( mapNodeName.equals( ELEM_SERVLET_NAME ) ) {
mappings.add( "srv:" + getTextFromNode( mapChild ) );
} else if ( mapNodeName.equals( ELEM_URL_PATTERN ) ) {
mappings.add( "url:" + getTextFromNode( mapChild ) );
} else if ( mapNodeName.equals( ELEM_DISPATCHER ) ) {
String dispatcherValue = getTextFromNode( mapChild );
if ( dispatcherValue.equals( DISPATCHER_REQUEST ) )
onRequest = true;
else if ( dispatcherValue.equals( DISPATCHER_FORWARD ) )
onForward = true;
else if ( dispatcherValue.equals( DISPATCHER_INCLUDE ) )
onInclude = true;
else if ( dispatcherValue.equals( DISPATCHER_ERROR ) )
onError = true;
}
}
if ( !onRequest && !onInclude && !onForward && !onError ) {
onRequest = true;
}
if ( mappings.isEmpty( ) ) {
throw new WinstoneException( Launcher.RESOURCES.getString(
"WebAppConfig.BadFilterMapping", filterName ) );
}
for ( Iterator i = mappings.iterator( ); i.hasNext( ); ) {
String item = ( String ) i.next( );
Mapping mapping = null;
try {
if ( item.startsWith( "srv:" ) ) {
mapping = Mapping.createFromLink( filterName, item.substring( 4 ) );
} else {
mapping = Mapping.createFromURL( filterName, item.substring( 4 ) );
}
if ( onRequest )
lfpRequest.add( mapping );
if ( onForward )
lfpForward.add( mapping );
if ( onInclude )
lfpInclude.add( mapping );
if ( onError )
lfpError.add( mapping );
} catch ( WinstoneException err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WebAppConfig.ErrorMapURL",
err.getMessage( ) );
}
}
}
// Process the list of welcome files
else if ( nodeName.equals( ELEM_WELCOME_FILES ) ) {
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node welcomeFile = child.getChildNodes( ).item( m );
if ( ( welcomeFile.getNodeType( ) == Node.ELEMENT_NODE )
&& welcomeFile.getNodeName( ).equals( ELEM_WELCOME_FILE ) ) {
String welcomeStr = getTextFromNode( welcomeFile );
if ( !welcomeStr.equals( "" ) ) {
localWelcomeFiles.add( welcomeStr );
}
}
}
}
// Process the error pages
else if ( nodeName.equals( ELEM_ERROR_PAGE ) ) {
String code = null;
String exception = null;
String location = null;
// Parse the element and extract
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node errorChild = child.getChildNodes( ).item( k );
if ( errorChild.getNodeType( ) != Node.ELEMENT_NODE )
continue;
String errorChildName = errorChild.getNodeName( );
if ( errorChildName.equals( ELEM_ERROR_CODE ) )
code = getTextFromNode( errorChild );
else if ( errorChildName.equals( ELEM_EXCEPTION_TYPE ) )
exception = getTextFromNode( errorChild );
else if ( errorChildName.equals( ELEM_ERROR_LOCATION ) )
location = getTextFromNode( errorChild );
}
if ( ( code != null ) && ( location != null ) )
this.errorPagesByCode.put( code.trim( ), location.trim( ) );
if ( ( exception != null ) && ( location != null ) )
try {
Class exceptionClass = Class.forName( exception
.trim( ), false, this.loader );
localErrorPagesByExceptionList.add( exceptionClass );
this.errorPagesByException.put( exceptionClass,
location.trim( ) );
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WebAppConfig.ExceptionNotFound",
exception );
}
}
// Process the list of welcome files
else if ( nodeName.equals( ELEM_MIME_MAPPING ) ) {
String extension = null;
String mimeType = null;
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node mimeTypeNode = child.getChildNodes( ).item( m );
if ( mimeTypeNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( mimeTypeNode.getNodeName( ).equals(
ELEM_MIME_EXTENSION ) )
extension = getTextFromNode( mimeTypeNode );
else if ( mimeTypeNode.getNodeName( ).equals(
ELEM_MIME_TYPE ) )
mimeType = getTextFromNode( mimeTypeNode );
}
if ( ( extension != null ) && ( mimeType != null ) )
this.mimeTypes.put( extension.toLowerCase( ), mimeType );
else
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.InvalidMimeMapping",
new String[] { extension, mimeType } );
}
// Process the list of welcome files
else if ( nodeName.equals( ELEM_CONTEXT_PARAM ) ) {
String name = null;
String value = null;
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node contextParamNode = child.getChildNodes( ).item( m );
if ( contextParamNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( contextParamNode.getNodeName( ).equals(
ELEM_PARAM_NAME ) )
name = getTextFromNode( contextParamNode );
else if ( contextParamNode.getNodeName( ).equals(
ELEM_PARAM_VALUE ) )
value = getTextFromNode( contextParamNode );
}
if ( ( name != null ) && ( value != null ) )
this.initParameters.put( name, value );
else
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.InvalidInitParam", new String[] {
name, value } );
}
// Process locale encoding mapping elements
else if ( nodeName.equals( ELEM_LOCALE_ENC_MAP_LIST ) ) {
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node mappingNode = child.getChildNodes( ).item( m );
if ( mappingNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( mappingNode.getNodeName( ).equals( ELEM_LOCALE_ENC_MAPPING ) ) {
String localeName = "";
String encoding = "";
for ( int l = 0; l < mappingNode.getChildNodes( ).getLength( ); l++ ) {
Node mappingChildNode = mappingNode.getChildNodes( ).item( l );
if ( mappingChildNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( mappingChildNode.getNodeName( ).equals( ELEM_LOCALE ) )
localeName = getTextFromNode( mappingChildNode );
else if ( mappingChildNode.getNodeName( ).equals( ELEM_ENCODING ) )
encoding = getTextFromNode( mappingChildNode );
}
if ( !encoding.equals( "" ) && !localeName.equals( "" ) )
this.localeEncodingMap.put( localeName, encoding );
}
}
}
// Record the url mappings for jsp files if set
else if ( nodeName.equals( ELEM_JSP_CONFIG ) ) {
for ( int m = 0; m < child.getChildNodes( ).getLength( ); m++ ) {
Node propertyGroupNode = child.getChildNodes( ).item( m );
if ( ( propertyGroupNode.getNodeType( ) == Node.ELEMENT_NODE )
&& propertyGroupNode.getNodeName( ).equals( ELEM_JSP_PROPERTY_GROUP ) ) {
for ( int l = 0; l < propertyGroupNode.getChildNodes( ).getLength( ); l++ ) {
Node urlPatternNode = propertyGroupNode.getChildNodes( ).item( l );
if ( ( urlPatternNode.getNodeType( ) == Node.ELEMENT_NODE )
&& urlPatternNode.getNodeName( ).equals( ELEM_URL_PATTERN ) ) {
String jm = getTextFromNode( urlPatternNode );
if ( !jm.equals( "" ) ) {
jspMappings.add( jm );
}
}
}
}
}
}
}
}
// If not distributable, remove the cluster reference
if ( !distributable && ( cluster != null ) ) {
Logger.log( Logger.INFO, Launcher.RESOURCES,
"WebAppConfig.ClusterOffNotDistributable", this.contextName );
} else {
this.cluster = cluster;
}
// Build the login/security role instance
if ( !constraintNodes.isEmpty( ) && ( loginConfigNode != null ) ) {
String authMethod = null;
for ( int n = 0; n < loginConfigNode.getChildNodes( ).getLength( ); n++ ) {
if ( loginConfigNode.getChildNodes( ).item( n ).getNodeName( ).equals( "auth-method" ) ) {
authMethod = getTextFromNode( loginConfigNode.getChildNodes( ).item( n ) );
}
}
// Load the appropriate auth class
if ( authMethod == null ) {
authMethod = "BASIC";
} else {
authMethod = WinstoneResourceBundle.globalReplace( authMethod, "-", "" );
}
String realmClassName = stringArg( startupArgs, "realmClassName",
DEFAULT_REALM_CLASS ).trim( );
String authClassName = "winstone.auth."
+ authMethod.substring( 0, 1 ).toUpperCase( )
+ authMethod.substring( 1 ).toLowerCase( )
+ "AuthenticationHandler";
try {
// Build the realm
Class realmClass = Class.forName( realmClassName, true, parentClassLoader );
Constructor realmConstr = realmClass.getConstructor(
new Class[] {Set.class, Map.class } );
this.authenticationRealm = ( AuthenticationRealm ) realmConstr.newInstance(
new Object[] { rolesAllowed, startupArgs } );
// Build the authentication handler
Class authClass = Class.forName( authClassName );
Constructor authConstr = authClass
.getConstructor( new Class[] { Node.class, List.class,
Set.class, AuthenticationRealm.class } );
this.authenticationHandler = ( AuthenticationHandler ) authConstr
.newInstance( new Object[] { loginConfigNode,
constraintNodes, rolesAllowed,
authenticationRealm } );
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.AuthDisabled", authMethod );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WebAppConfig.AuthError", new String[] { authClassName,
realmClassName }, err );
}
} else if ( !stringArg( startupArgs, "realmClassName", "" ).trim( ).equals( "" ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WebAppConfig.NoWebXMLSecurityDefs" );
}
// Instantiate the JNDI manager
String jndiMgrClassName = stringArg( startupArgs, "webappJndiClassName",
DEFAULT_JNDI_MGR_CLASS ).trim( );
if ( useJNDI ) {
try {
// Build the realm
Class jndiMgrClass = Class.forName( jndiMgrClassName, true, parentClassLoader );
Constructor jndiMgrConstr = jndiMgrClass.getConstructor( new Class[] {
Map.class, List.class, ClassLoader.class } );
this.jndiManager = ( JNDIManager ) jndiMgrConstr.newInstance( new Object[] {
null, envEntryNodes, this.loader } );
if ( this.jndiManager != null )
this.jndiManager.setup( );
} catch ( ClassNotFoundException err ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.JNDIDisabled" );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WebAppConfig.JNDIError", jndiMgrClassName, err );
}
}
String loggerClassName = stringArg( startupArgs, "accessLoggerClassName", "" ).trim( );
if ( !loggerClassName.equals( "" ) ) {
try {
// Build the realm
Class loggerClass = Class.forName( loggerClassName, true, parentClassLoader );
Constructor loggerConstr = loggerClass.getConstructor( new Class[] {
WebAppConfiguration.class, Map.class } );
this.accessLogger = ( AccessLogger ) loggerConstr.newInstance( new Object[] {
this, startupArgs} );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WebAppConfig.LoggerError", loggerClassName, err );
}
} else {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WebAppConfig.LoggerDisabled" );
}
// Add the default index.html welcomeFile if none are supplied
if ( localWelcomeFiles.isEmpty( ) ) {
if ( useJasper ) {
localWelcomeFiles.add( "index.jsp" );
}
localWelcomeFiles.add( "index.html" );
}
// Put the name filters after the url filters, then convert to string arrays
this.filterPatternsRequest = ( Mapping[] ) lfpRequest.toArray( new Mapping[0] );
this.filterPatternsForward = ( Mapping[] ) lfpForward.toArray( new Mapping[0] );
this.filterPatternsInclude = ( Mapping[] ) lfpInclude.toArray( new Mapping[0] );
this.filterPatternsError = ( Mapping[] ) lfpError.toArray( new Mapping[0] );
if ( this.filterPatternsRequest.length > 0 )
Arrays.sort( this.filterPatternsRequest, this.filterPatternsRequest[0] );
if ( this.filterPatternsForward.length > 0 )
Arrays.sort( this.filterPatternsForward, this.filterPatternsForward[0] );
if ( this.filterPatternsInclude.length > 0 )
Arrays.sort( this.filterPatternsInclude, this.filterPatternsInclude[0] );
if ( this.filterPatternsError.length > 0 )
Arrays.sort( this.filterPatternsError, this.filterPatternsError[0] );
this.welcomeFiles = ( String[] ) localWelcomeFiles.toArray( new String[0] );
this.errorPagesByExceptionKeysSorted = ( Class[] ) localErrorPagesByExceptionList
.toArray( new Class[0] );
Arrays.sort( this.errorPagesByExceptionKeysSorted, this );
// Put the listeners into their arrays
this.contextAttributeListeners = ( ServletContextAttributeListener[] ) contextAttributeListeners
.toArray( new ServletContextAttributeListener[0] );
this.contextListeners = ( ServletContextListener[] ) contextListeners
.toArray( new ServletContextListener[0] );
this.requestListeners = ( ServletRequestListener[] ) requestListeners
.toArray( new ServletRequestListener[0] );
this.requestAttributeListeners = ( ServletRequestAttributeListener[] ) requestAttributeListeners
.toArray( new ServletRequestAttributeListener[0] );
this.sessionActivationListeners = ( HttpSessionActivationListener[] ) sessionActivationListeners
.toArray( new HttpSessionActivationListener[0] );
this.sessionAttributeListeners = ( HttpSessionAttributeListener[] ) sessionAttributeListeners
.toArray( new HttpSessionAttributeListener[0] );
this.sessionListeners = ( HttpSessionListener[] ) sessionListeners
.toArray( new HttpSessionListener[0] );
// If we haven't explicitly mapped the default servlet, map it here
if ( this.defaultServletName == null )
this.defaultServletName = DEFAULT_SERVLET_NAME;
if ( this.errorServletName == null )
this.errorServletName = ERROR_SERVLET_NAME;
// If we don't have an instance of the default servlet, mount the inbuilt one
if ( this.servletInstances.get( this.defaultServletName ) == null ) {
boolean useDirLists = booleanArg( startupArgs, "directoryListings", true );
Map staticParams = new Hashtable( );
staticParams.put( "webRoot", webRoot );
staticParams.put( "prefix", this.prefix );
staticParams.put( "directoryList", "" + useDirLists );
ServletConfiguration defaultServlet = new ServletConfiguration(
this, this.defaultServletName, DEFAULT_SERVLET_CLASS,
staticParams, 0 );
this.servletInstances.put( this.defaultServletName, defaultServlet );
startupServlets.add( defaultServlet );
}
// If we don't have an instance of the default servlet, mount the inbuilt one
if ( this.servletInstances.get( this.errorServletName ) == null ) {
ServletConfiguration errorServlet = new ServletConfiguration(
this, this.errorServletName, ERROR_SERVLET_CLASS,
new HashMap( ), 0 );
this.servletInstances.put( this.errorServletName, errorServlet );
startupServlets.add( errorServlet );
}
// Initialise jasper servlet if requested
if ( useJasper ) {
setAttribute( "org.apache.catalina.classloader", this.loader );
try {
StringBuffer cp = new StringBuffer( );
for ( Iterator i = localLoaderClassPathFiles.iterator( ); i.hasNext( ); ) {
cp.append( ( ( File ) i.next( ) ).getCanonicalPath( ) ).append(
File.pathSeparatorChar );
}
for ( int n = 0; n < parentClassPaths.length; n++ ) {
cp.append( parentClassPaths[n].getCanonicalPath( ) ).append(
File.pathSeparatorChar );
}
setAttribute( "org.apache.catalina.jsp_classpath",
( cp.length( ) > 0 ? cp.substring( 0, cp.length( ) - 1 ) : "" ) );
} catch ( IOException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ErrorSettingJSPPaths", err );
}
Map jspParams = new HashMap( );
addJspServletParams( jspParams );
ServletConfiguration sc = new ServletConfiguration( this,
JSP_SERVLET_NAME, JSP_SERVLET_CLASS, jspParams, 3 );
this.servletInstances.put( JSP_SERVLET_NAME, sc );
startupServlets.add( sc );
for ( Iterator mapIt = jspMappings.iterator( ); mapIt.hasNext( ); ) {
processMapping( JSP_SERVLET_NAME, ( String ) mapIt.next( ),
this.exactServletMatchMounts, localFolderPatterns,
localExtensionPatterns );
}
}
// Initialise invoker servlet if requested
if ( useInvoker ) {
// Get generic options
String invokerPrefix = stringArg( startupArgs, "invokerPrefix",
DEFAULT_INVOKER_PREFIX );
Map invokerParams = new HashMap( );
invokerParams.put( "prefix", this.prefix );
invokerParams.put( "invokerPrefix", invokerPrefix );
ServletConfiguration sc = new ServletConfiguration( this,
INVOKER_SERVLET_NAME, INVOKER_SERVLET_CLASS,
invokerParams, 3 );
this.servletInstances.put( INVOKER_SERVLET_NAME, sc );
processMapping( INVOKER_SERVLET_NAME, invokerPrefix + Mapping.STAR,
this.exactServletMatchMounts, localFolderPatterns,
localExtensionPatterns );
}
// Sort the folder patterns so the longest paths are first
localFolderPatterns.addAll( localExtensionPatterns );
this.patternMatches = ( Mapping[] ) localFolderPatterns.toArray( new Mapping[0] );
if ( this.patternMatches.length > 0 )
Arrays.sort( this.patternMatches, this.patternMatches[0] );
// Send init notifies
try {
for ( int n = 0; n < this.contextListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.loader );
this.contextListeners[n].contextInitialized( new ServletContextEvent( this ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ContextStartupError", this.contextName, err );
this.contextStartupError = err;
}
if ( this.contextStartupError == null ) {
// Load sessions if enabled
if ( this.useSavedSessions ) {
WinstoneSession.loadSessions( this );
}
// Initialise all the filters
for ( Iterator i = this.filterInstances.values( ).iterator( ); i.hasNext( ); ) {
FilterConfiguration config = ( FilterConfiguration ) i.next( );
try {
config.getFilter( );
} catch ( ServletException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.FilterStartupError",
config.getFilterName( ), err );
}
}
// Initialise load on startup servlets
Object autoStarters[] = startupServlets.toArray( );
Arrays.sort( autoStarters );
for ( int n = 0; n < autoStarters.length; n++ ) {
( ( ServletConfiguration ) autoStarters[n] ).ensureInitialization( );
}
}
}
/**
* Build the web-app classloader. This tries to load the preferred classloader first,
* but if it fails, falls back to a simple URLClassLoader.
*/
927 private ClassLoader buildWebAppClassLoader( Map startupArgs, ClassLoader parentClassLoader,
928 String webRoot, List classPathFileList ) {
List urlList = new ArrayList( );
try {
// Web-inf folder
File webInfFolder = new File( webRoot, WEB_INF );
// Classes folder
File classesFolder = new File( webInfFolder, CLASSES );
if ( classesFolder.exists( ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.WebAppClasses" );
String classesFolderURL = classesFolder.getCanonicalFile( ).toURL( ).toString( );
urlList.add( new URL( classesFolderURL.endsWith( "/" ) ? classesFolderURL : classesFolderURL + "/" ) );
classPathFileList.add( classesFolder );
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.NoWebAppClasses",
classesFolder.toString( ) );
}
// Lib folder's jar files
File libFolder = new File( webInfFolder, LIB );
if ( libFolder.exists( ) ) {
File jars[] = libFolder.listFiles( );
for ( int n = 0; n < jars.length; n++ ) {
String jarName = jars[n].getName( ).toLowerCase( );
if ( jarName.endsWith( ".jar" ) || jarName.endsWith( ".zip" ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.WebAppLib", jars[n].getName( ) );
urlList.add( jars[n].toURL( ) );
classPathFileList.add( jars[n] );
}
}
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WebAppConfig.NoWebAppLib", libFolder
.toString( ) );
}
} catch ( MalformedURLException err ) {
throw new WinstoneException( Launcher.RESOURCES
.getString( "WebAppConfig.BadURL" ), err );
} catch ( IOException err ) {
throw new WinstoneException( Launcher.RESOURCES
.getString( "WebAppConfig.IOException" ), err );
}
URL jarURLs[] = ( URL [] ) urlList.toArray( new URL[urlList.size( )] );
String preferredClassLoader = stringArg( startupArgs, "preferredClassLoader", WEBAPP_CL_CLASS );
if ( booleanArg( startupArgs, "useServletReloading", false ) &&
stringArg( startupArgs, "preferredClassLoader", "" ).equals( "" ) ) {
preferredClassLoader = RELOADING_CL_CLASS;
}
// Try to set up the preferred class loader, and if we fail, use the normal one
ClassLoader outputCL = null;
if ( !preferredClassLoader.equals( "" ) ) {
try {
Class preferredCL = Class.forName( preferredClassLoader, true, parentClassLoader );
Constructor reloadConstr = preferredCL.getConstructor( new Class[] {
URL[].class, ClassLoader.class} );
outputCL = ( ClassLoader ) reloadConstr.newInstance( new Object[] {
jarURLs, parentClassLoader} );
} catch ( Throwable err ) {
if ( !stringArg( startupArgs, "preferredClassLoader", "" ).equals( "" ) ||
!preferredClassLoader.equals( WEBAPP_CL_CLASS ) ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.CLError", err );
}
}
}
if ( outputCL == null ) {
outputCL = new URLClassLoader( jarURLs, parentClassLoader );
}
Logger.log( Logger.MAX, Launcher.RESOURCES, "WebAppConfig.WebInfClassLoader", outputCL.toString( ) );
return outputCL;
}
1008 private void addListenerInstance( Object listenerInstance, List contextAttributeListeners,
1009 List contextListeners, List requestAttributeListeners, List requestListeners,
1010 List sessionActivationListeners, List sessionAttributeListeners,
1011 List sessionListeners ) {
if ( listenerInstance instanceof ServletContextAttributeListener )
contextAttributeListeners.add( listenerInstance );
if ( listenerInstance instanceof ServletContextListener )
contextListeners.add( listenerInstance );
if ( listenerInstance instanceof ServletRequestAttributeListener )
requestAttributeListeners.add( listenerInstance );
if ( listenerInstance instanceof ServletRequestListener )
requestListeners.add( listenerInstance );
if ( listenerInstance instanceof HttpSessionActivationListener )
sessionActivationListeners.add( listenerInstance );
if ( listenerInstance instanceof HttpSessionAttributeListener )
sessionAttributeListeners.add( listenerInstance );
if ( listenerInstance instanceof HttpSessionListener )
sessionListeners.add( listenerInstance );
}
1028 public String getContextPath( ) {
return this.prefix;
}
1032 public String getWebroot( ) {
return this.webRoot;
}
1036 public ClassLoader getLoader( ) {
return this.loader;
}
1040 public AccessLogger getAccessLogger( ) {
return this.accessLogger;
}
1044 public Map getFilters( ) {
return this.filterInstances;
}
1048 public String getContextName( ) {
return this.contextName;
}
1052 public Class[] getErrorPageExceptions( ) {
return this.errorPagesByExceptionKeysSorted;
}
1056 public Map getErrorPagesByException( ) {
return this.errorPagesByException;
}
1060 public Map getErrorPagesByCode( ) {
return this.errorPagesByCode;
}
1064 public Map getLocaleEncodingMap( ) {
return this.localeEncodingMap;
}
1068 public String[] getWelcomeFiles( ) {
return this.welcomeFiles;
}
1072 public boolean isDistributable( ) {
return ( this.cluster != null );
}
1076 public Map getFilterMatchCache( ) {
return this.filterMatchCache;
}
1080 public String getOwnerHostname( ) {
return this.ownerHostConfig.getHostname( );
}
1084 public ServletRequestListener[] getRequestListeners( ) {
return this.requestListeners;
}
1088 public ServletRequestAttributeListener[] getRequestAttributeListeners( ) {
return this.requestAttributeListeners;
}
1092 public static void addJspServletParams( Map jspParams ) {
jspParams.put( "logVerbosityLevel", JSP_SERVLET_LOG_LEVEL );
jspParams.put( "fork", "false" );
}
1097 public int compare( Object one, Object two ) {
if ( !( one instanceof Class ) || !( two instanceof Class ) )
throw new IllegalArgumentException(
"This comparator is only for sorting classes" );
Class classOne = ( Class ) one;
Class classTwo = ( Class ) two;
if ( classOne.isAssignableFrom( classTwo ) )
return 1;
else if ( classTwo.isAssignableFrom( classOne ) )
return -1;
else
return 0;
}
1111 public String getServletURIFromRequestURI( String requestURI ) {
if ( prefix.equals( "" ) ) {
return requestURI;
} else if ( requestURI.startsWith( prefix ) ) {
return requestURI.substring( prefix.length( ) );
} else {
throw new WinstoneException( "This shouldn't happen, " +
"since we aborted earlier if we didn't match" );
}
}
/**
* Iterates through each of the servlets/filters and calls destroy on them
*/
1125 public void destroy( ) {
synchronized ( this.filterMatchCache ) {
this.filterMatchCache.clear( );
}
Collection filterInstances = new ArrayList( this.filterInstances.values( ) );
for ( Iterator i = filterInstances.iterator( ); i.hasNext( ); ) {
try {
( ( FilterConfiguration ) i.next( ) ).destroy( );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ShutdownError", err );
}
}
this.filterInstances.clear( );
Collection servletInstances = new ArrayList( this.servletInstances.values( ) );
for ( Iterator i = servletInstances.iterator( ); i.hasNext( ); ) {
try {
( ( ServletConfiguration ) i.next( ) ).destroy( );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ShutdownError", err );
}
}
this.servletInstances.clear( );
// Drop all sessions
Collection sessions = new ArrayList( this.sessions.values( ) );
for ( Iterator i = sessions.iterator( ); i.hasNext( ); ) {
WinstoneSession session = ( WinstoneSession ) i.next( );
try {
if ( this.useSavedSessions ) {
session.saveToTemp( );
} else {
session.invalidate( );
}
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ShutdownError", err );
}
}
this.sessions.clear( );
// Send destroy notifies - backwards
for ( int n = this.contextListeners.length - 1; n >= 0; n-- ) {
try {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.loader );
this.contextListeners[n].contextDestroyed( new ServletContextEvent( this ) );
this.contextListeners[n] = null;
Thread.currentThread( ).setContextClassLoader( cl );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ShutdownError", err );
}
}
this.contextListeners = null;
// Terminate class loader reloading thread if running
if ( this.loader != null ) {
// already shutdown/handled by the servlet context listeners
// try {
// Method methDestroy = this.loader.getClass( ).getMethod( "destroy", new Class[0] );
// methDestroy.invoke( this.loader, new Object[0] );
// } catch ( Throwable err ) {
// Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ShutdownError", err );
// }
this.loader = null;
}
// Kill JNDI manager if we have one
if ( this.jndiManager != null ) {
this.jndiManager.tearDown( );
this.jndiManager = null;
}
// Kill JNDI manager if we have one
if ( this.accessLogger != null ) {
this.accessLogger.destroy( );
this.accessLogger = null;
}
}
/**
* Triggered by the admin thread on the reloading class loader. This will
* cause a full shutdown and reinstantiation of the web app - not real
* graceful, but you shouldn't have reloading turned on in high load
* environments.
*/
1211 public void resetClassLoader( ) throws IOException {
this.ownerHostConfig.reloadWebApp( getContextPath( ) );
}
/**
* Here we process url patterns into the exactMatch and patternMatch lists
*/
1218 private void processMapping( String name, String pattern, Map exactPatterns,
1219 List folderPatterns, List extensionPatterns ) {
Mapping urlPattern = null;
try {
urlPattern = Mapping.createFromURL( name, pattern );
} catch ( WinstoneException err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WebAppConfig.ErrorMapURL",
err.getMessage( ) );
return;
}
// put the pattern in the correct list
if ( urlPattern.getPatternType( ) == Mapping.EXACT_PATTERN ) {
exactPatterns.put( urlPattern.getUrlPattern( ), name );
} else if ( urlPattern.getPatternType( ) == Mapping.FOLDER_PATTERN ) {
folderPatterns.add( urlPattern );
} else if ( urlPattern.getPatternType( ) == Mapping.EXTENSION_PATTERN ) {
extensionPatterns.add( urlPattern );
} else if ( urlPattern.getPatternType( ) == Mapping.DEFAULT_SERVLET ) {
this.defaultServletName = name;
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WebAppConfig.InvalidMount",
new String[] { name, pattern } );
}
}
/**
* Execute the pattern match, and try to return a servlet that matches this
* URL
*/
1249 private ServletConfiguration urlMatch( String path,
1250 StringBuffer servletPath, StringBuffer pathInfo ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WebAppConfig.URLMatch", path );
// Check exact matches first
String exact = ( String ) this.exactServletMatchMounts.get( path );
if ( exact != null ) {
if ( this.servletInstances.get( exact ) != null ) {
servletPath.append( WinstoneRequest.decodeURLToken( path ) );
// pathInfo.append( "" ); // a hack - empty becomes null later
return ( ServletConfiguration ) this.servletInstances.get( exact );
}
}
// Inexact mount check
for ( int n = 0; n < this.patternMatches.length; n++ ) {
Mapping urlPattern = this.patternMatches[n];
if ( urlPattern.match( path, servletPath, pathInfo ) &&
( this.servletInstances.get( urlPattern.getMappedTo( ) ) != null ) ) {
return ( ServletConfiguration ) this.servletInstances
.get( urlPattern.getMappedTo( ) );
}
}
// return default servlet
// servletPath.append( "" ); // unneeded
if ( this.servletInstances.get( this.defaultServletName ) == null )
throw new WinstoneException( Launcher.RESOURCES.getString(
"WebAppConfig.MatchedNonExistServlet",
this.defaultServletName ) );
// pathInfo.append( path );
servletPath.append( WinstoneRequest.decodeURLToken( path ) );
return ( ServletConfiguration ) this.servletInstances.get( this.defaultServletName );
}
/**
* Constructs a session instance with the given sessionId
*
* @param sessionId The sessionID for the new session
* @return A valid session object
*/
1290 public WinstoneSession makeNewSession( String sessionId ) {
WinstoneSession ws = new WinstoneSession( sessionId );
ws.setWebAppConfiguration( this );
setSessionListeners( ws );
if ( ( this.sessionTimeout != null ) && ( this.sessionTimeout.intValue( ) > 0 ) ) {
ws.setMaxInactiveInterval( this.sessionTimeout.intValue( ) * 60 );
} else {
ws.setMaxInactiveInterval( -1 );
}
ws.setLastAccessedDate( System.currentTimeMillis( ) );
ws.sendCreatedNotifies( );
this.sessions.put( sessionId, ws );
return ws;
}
/**
* Retrieves the session by id. If the web app is distributable, it asks the
* other members of the cluster if it doesn't have it itself.
*
* @param sessionId The id of the session we want
* @return A valid session instance
*/
1312 public WinstoneSession getSessionById( String sessionId, boolean localOnly ) {
if ( sessionId == null ) {
return null;
}
WinstoneSession session = ( WinstoneSession ) this.sessions.get( sessionId );
if ( session != null ) {
return session;
}
// If I'm distributable ... check remotely
if ( ( this.cluster != null ) && !localOnly ) {
session = this.cluster.askClusterForSession( sessionId, this );
if ( session != null ) {
this.sessions.put( sessionId, session );
}
return session;
} else {
return null;
}
}
/**
* Add/Remove the session from the collection
*/
1336 void removeSessionById( String sessionId ) {
this.sessions.remove( sessionId );
}
1339 void addSession( String sessionId, WinstoneSession session ) {
this.sessions.put( sessionId, session );
}
1343 public void invalidateExpiredSessions( ) {
Object allSessions[] = this.sessions.values( ).toArray( );
int expiredCount = 0;
for ( int n = 0; n < allSessions.length; n++ ) {
WinstoneSession session = ( WinstoneSession ) allSessions[n];
if ( !session.isNew( ) && session.isUnusedByRequests( ) && session.isExpired( ) ) {
session.invalidate( );
expiredCount++;
}
}
if ( expiredCount > 0 ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.InvalidatedSessions", expiredCount + "" );
}
}
1360 public void setSessionListeners( WinstoneSession session ) {
session.setSessionActivationListeners( this.sessionActivationListeners );
session.setSessionAttributeListeners( this.sessionAttributeListeners );
session.setSessionListeners( this.sessionListeners );
}
1366 public void removeServletConfigurationAndMappings( ServletConfiguration config ) {
this.servletInstances.remove( config.getServletName( ) );
// The urlMatch method will only match to non-null mappings, so we don't need
// to remove anything here
}
/***************************************************************************
*
* OK ... from here to the end is the interface implementation methods for
* the servletContext interface.
*
**************************************************************************/
// Application level attributes
1380 public Object getAttribute( String name ) {
return this.attributes.get( name );
}
1384 public Enumeration getAttributeNames( ) {
return Collections.enumeration( this.attributes.keySet( ) );
}
1388 public void removeAttribute( String name ) {
Object me = this.attributes.get( name );
this.attributes.remove( name );
if ( me != null )
for ( int n = 0; n < this.contextAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( getLoader( ) );
this.contextAttributeListeners[n].attributeRemoved(
new ServletContextAttributeEvent( this, name, me ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
1401 public void setAttribute( String name, Object object ) {
if ( object == null ) {
removeAttribute( name );
} else {
Object me = this.attributes.get( name );
this.attributes.put( name, object );
if ( me != null ) {
for ( int n = 0; n < this.contextAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( getLoader( ) );
this.contextAttributeListeners[n].attributeReplaced(
new ServletContextAttributeEvent( this, name, me ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
} else {
for ( int n = 0; n < this.contextAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( getLoader( ) );
this.contextAttributeListeners[n].attributeAdded(
new ServletContextAttributeEvent( this, name, object ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
}
}
// Application level init parameters
1428 public String getInitParameter( String name ) {
return ( String ) this.initParameters.get( name );
}
1432 public Enumeration getInitParameterNames( ) {
return Collections.enumeration( this.initParameters.keySet( ) );
}
// Server info
1437 public String getServerInfo( ) {
return Launcher.RESOURCES.getString( "ServerVersion" );
}
1441 public int getMajorVersion( ) {
return 2;
}
1445 public int getMinorVersion( ) {
return 5;
}
// Weird mostly deprecated crap to do with getting servlet instances
1450 public javax.servlet.ServletContext getContext( String uri ) {
return this.ownerHostConfig.getWebAppByURI( uri );
}
1454 public String getServletContextName( ) {
return this.displayName;
}
/**
* Look up the map of mimeType extensions, and return the type that matches
*/
1461 public String getMimeType( String fileName ) {
int dotPos = fileName.lastIndexOf( '.' );
if ( ( dotPos != -1 ) && ( dotPos != fileName.length( ) - 1 ) ) {
String extension = fileName.substring( dotPos + 1 ).toLowerCase( );
String mimeType = ( String ) this.mimeTypes.get( extension );
return mimeType;
} else
return null;
}
// Context level log statements
1472 public void log( String message ) {
Logger.logDirectMessage( Logger.INFO, this.contextName, message, null );
}
1476 public void log( String message, Throwable throwable ) {
Logger.logDirectMessage( Logger.ERROR, this.contextName, message, throwable );
}
/**
* Named dispatcher - this basically gets us a simple exact dispatcher ( no
* url matching, no request attributes and no security )
*/
1484 public javax.servlet.RequestDispatcher getNamedDispatcher( String name ) {
ServletConfiguration servlet = ( ServletConfiguration ) this.servletInstances.get( name );
if ( servlet != null ) {
RequestDispatcher rd = new RequestDispatcher( this, servlet );
if ( rd != null ) {
rd.setForNamedDispatcher( this.filterPatternsForward, this.filterPatternsInclude );
return rd;
}
}
return null;
}
/**
* Gets a dispatcher, which sets the request attributes, etc on a
* forward/include. Doesn't execute security though.
*/
1500 public javax.servlet.RequestDispatcher getRequestDispatcher(
1501 String uriInsideWebapp ) {
if ( uriInsideWebapp == null ) {
return null;
} else if ( !uriInsideWebapp.startsWith( "/" ) ) {
return null;
}
// Parse the url for query string, etc
String queryString = "";
int questionPos = uriInsideWebapp.indexOf( '?' );
if ( questionPos != -1 ) {
if ( questionPos != uriInsideWebapp.length( ) - 1 ) {
queryString = uriInsideWebapp.substring( questionPos + 1 );
}
uriInsideWebapp = uriInsideWebapp.substring( 0, questionPos );
}
// Return the dispatcher
StringBuffer servletPath = new StringBuffer( );
StringBuffer pathInfo = new StringBuffer( );
ServletConfiguration servlet = urlMatch( uriInsideWebapp, servletPath, pathInfo );
if ( servlet != null ) {
RequestDispatcher rd = new RequestDispatcher( this, servlet );
if ( rd != null ) {
rd.setForURLDispatcher( servletPath.toString( ), pathInfo.toString( )
.equals( "" ) ? null : pathInfo.toString( ), queryString,
uriInsideWebapp, this.filterPatternsForward,
this.filterPatternsInclude );
return rd;
}
}
return null;
}
/**
* Creates the dispatcher that corresponds to a request level dispatch ( ie
* the initial entry point ). The difference here is that we need to set up
* the dispatcher so that on a forward, it executes the security checks and
* the request filters, while not setting any of the request attributes for
* a forward. Also, we can't return a null dispatcher in error case - instead
* we have to return a dispatcher pre-init'd for showing an error page ( eg 404 ).
* A null dispatcher is interpreted to mean a successful 302 has occurred.
*/
1544 public RequestDispatcher getInitialDispatcher( String uriInsideWebapp,
1545 WinstoneRequest request, WinstoneResponse response )
throws IOException {
if ( !uriInsideWebapp.equals( "" ) && !uriInsideWebapp.startsWith( "/" ) ) {
return this.getErrorDispatcherByCode(
HttpServletResponse.SC_BAD_REQUEST,
Launcher.RESOURCES.getString( "WebAppConfig.InvalidURI", uriInsideWebapp ),
null );
} else if ( this.contextStartupError != null ) {
StringWriter sw = new StringWriter( );
PrintWriter pw = new PrintWriter( sw, true );
this.contextStartupError.printStackTrace( pw );
return this.getErrorDispatcherByCode(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
Launcher.RESOURCES.getString( "WebAppConfig.ErrorDuringStartup", sw.toString( ) ),
this.contextStartupError );
}
// Parse the url for query string, etc
String queryString = "";
int questionPos = uriInsideWebapp.indexOf( '?' );
if ( questionPos != -1 ) {
if ( questionPos != uriInsideWebapp.length( ) - 1 )
queryString = uriInsideWebapp.substring( questionPos + 1 );
uriInsideWebapp = uriInsideWebapp.substring( 0, questionPos );
}
// Return the dispatcher
StringBuffer servletPath = new StringBuffer( );
StringBuffer pathInfo = new StringBuffer( );
ServletConfiguration servlet = urlMatch( uriInsideWebapp, servletPath, pathInfo );
if ( servlet != null ) {
// If the default servlet was returned, we should check for welcome files
if ( servlet.getServletName( ).equals( this.defaultServletName ) ) {
// Is path a directory ?
String directoryPath = servletPath.toString( );
if ( directoryPath.endsWith( "/" ) ) {
directoryPath = directoryPath.substring( 0, directoryPath.length( ) - 1 );
}
if ( directoryPath.startsWith( "/" ) ) {
directoryPath = directoryPath.substring( 1 );
}
File res = new File( webRoot, directoryPath );
if ( res.exists( ) && res.isDirectory( ) &&
( request.getMethod( ).equals( "GET" ) || request.getMethod( ).equals( "HEAD" ) ) ) {
// Check for the send back with slash case
if ( !servletPath.toString( ).endsWith( "/" ) ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WebAppConfig.FoundNonSlashDirectory", servletPath.toString( ) );
response.sendRedirect( this.prefix
+ servletPath.toString( )
+ pathInfo.toString( )
+ "/"
+ ( queryString.equals( "" ) ? "" : "?" + queryString ) );
return null;
}
// Check for welcome files
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WebAppConfig.CheckWelcomeFile", servletPath.toString( )
+ pathInfo.toString( ) );
String welcomeFile = matchWelcomeFiles( servletPath.toString( )
+ pathInfo.toString( ), request, queryString );
if ( welcomeFile != null ) {
response.sendRedirect( this.prefix + welcomeFile );
// + servletPath.toString( )
// + pathInfo.toString( )
// + welcomeFile
// + ( queryString.equals( "" ) ? "" : "?" + queryString ) );
return null;
}
}
}
RequestDispatcher rd = new RequestDispatcher( this, servlet );
rd.setForInitialDispatcher( servletPath.toString( ),
pathInfo.toString( ).equals( "" ) ? null : pathInfo.toString( ), queryString,
uriInsideWebapp, this.filterPatternsRequest, this.authenticationHandler );
return rd;
}
// If we are here, return a 404
return this.getErrorDispatcherByCode( HttpServletResponse.SC_NOT_FOUND,
Launcher.RESOURCES.getString( "StaticResourceServlet.PathNotFound",
uriInsideWebapp ), null );
}
/**
* Gets a dispatcher, set up for error dispatch.
*/
1635 public RequestDispatcher getErrorDispatcherByClass(
1636 Throwable exception ) {
// Check for exception class match
Class exceptionClasses[] = this.errorPagesByExceptionKeysSorted;
Throwable errWrapper = new ServletException( exception );
while ( errWrapper instanceof ServletException ) {
errWrapper = ( ( ServletException ) errWrapper ).getRootCause( );
if ( errWrapper == null ) {
break;
}
for ( int n = 0; n < exceptionClasses.length; n++ ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneResponse.TestingException",
new String[] {this.errorPagesByExceptionKeysSorted[n].getName( ),
errWrapper.getClass( ).getName( )} );
if ( exceptionClasses[n].isInstance( errWrapper ) ) {
String errorURI = ( String ) this.errorPagesByException.get( exceptionClasses[n] );
if ( errorURI != null ) {
RequestDispatcher rd = buildErrorDispatcher( errorURI,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
null, errWrapper );
if ( rd != null ) {
return rd;
}
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneResponse.SkippingException",
new String[] {exceptionClasses[n].getName( ),
( String ) this.errorPagesByException.get( exceptionClasses[n] ) } );
}
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneResponse.ExceptionNotMatched",
exceptionClasses[n].getName( ) );
}
}
}
// Otherwise throw a code error
Throwable errPassDown = exception;
while ( ( errPassDown instanceof ServletException ) &&
( ( ( ServletException ) errPassDown ).getRootCause( ) != null ) ) {
errPassDown = ( ( ServletException ) errPassDown ).getRootCause( );
}
return getErrorDispatcherByCode( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
null, errPassDown );
}
1686 public RequestDispatcher getErrorDispatcherByCode(
1687 int statusCode, String summaryMessage, Throwable exception ) {
// Check for status code match
String errorURI = ( String ) getErrorPagesByCode( ).get( "" + statusCode );
if ( errorURI != null ) {
RequestDispatcher rd = buildErrorDispatcher( errorURI, statusCode,
summaryMessage, exception );
if ( rd != null ) {
return rd;
}
}
// If no dispatcher available, return a dispatcher to the default error formatter
ServletConfiguration errorServlet = ( ServletConfiguration )
this.servletInstances.get( this.errorServletName );
if ( errorServlet != null ) {
RequestDispatcher rd = new RequestDispatcher( this, errorServlet );
if ( rd != null ) {
rd.setForErrorDispatcher( null, null, null, statusCode,
summaryMessage, exception, null, this.filterPatternsError );
return rd;
}
}
// Otherwise log and return null
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.NoErrorServlet", "" + statusCode, exception );
return null;
}
/**
* Build a dispatcher to the error handler if it's available. If it fails, return null.
*/
1718 private RequestDispatcher buildErrorDispatcher( String errorURI, int statusCode,
1719 String summaryMessage, Throwable exception ) {
// Parse the url for query string, etc
String queryString = "";
int questionPos = errorURI.indexOf( '?' );
if ( questionPos != -1 ) {
if ( questionPos != errorURI.length( ) - 1 ) {
queryString = errorURI.substring( questionPos + 1 );
}
errorURI = errorURI.substring( 0, questionPos );
}
// Get the message by recursing if none supplied
ServletException errIterator = new ServletException( exception );
while ( ( summaryMessage == null ) && ( errIterator != null ) ) {
summaryMessage = errIterator.getMessage( );
if ( errIterator.getRootCause( ) instanceof ServletException ) {
errIterator = ( ServletException ) errIterator.getRootCause( );
} else {
if ( summaryMessage == null ) {
summaryMessage = errIterator.getRootCause( ).getMessage( );
}
errIterator = null;
}
}
// Return the dispatcher
StringBuffer servletPath = new StringBuffer( );
StringBuffer pathInfo = new StringBuffer( );
ServletConfiguration servlet = urlMatch( errorURI, servletPath, pathInfo );
if ( servlet != null ) {
RequestDispatcher rd = new RequestDispatcher( this, servlet );
if ( rd != null ) {
rd.setForErrorDispatcher( servletPath.toString( ),
pathInfo.toString( ).equals( "" ) ? null : pathInfo.toString( ),
queryString, statusCode, summaryMessage,
exception, errorURI, this.filterPatternsError );
return rd;
}
}
return null;
}
/**
* Check if any of the welcome files under this path are available. Returns the
* name of the file if found, null otherwise. Returns the full internal webapp uri
*/
1765 private String matchWelcomeFiles( String path, WinstoneRequest request, String queryString ) {
if ( !path.endsWith( "/" ) ) {
path = path + "/";
}
String qs = ( queryString.equals( "" ) ? "" : "?" + queryString );
for ( int n = 0; n < this.welcomeFiles.length; n++ ) {
String welcomeFile = this.welcomeFiles[n];
while ( welcomeFile.startsWith( "/" ) ) {
welcomeFile = welcomeFile.substring( 1 );
}
welcomeFile = path + welcomeFile;
String exact = ( String ) this.exactServletMatchMounts.get( welcomeFile );
if ( exact != null ) {
return welcomeFile + qs;
}
// Inexact folder mount check - note folder mounts only
for ( int j = 0; j < this.patternMatches.length; j++ ) {
Mapping urlPattern = this.patternMatches[j];
if ( ( urlPattern.getPatternType( ) == Mapping.FOLDER_PATTERN )
&& urlPattern.match( welcomeFile, null, null ) ) {
return welcomeFile + qs;
}
}
try {
if ( getResource( welcomeFile ) != null ) {
return welcomeFile + qs;
}
} catch ( MalformedURLException err ) {}
}
return null;
}
// Getting resources via the classloader
1802 public URL getResource( String path ) throws MalformedURLException {
if ( path == null ) {
return null;
} else if ( !path.startsWith( "/" ) ) {
throw new MalformedURLException( Launcher.RESOURCES.getString(
"WebAppConfig.BadResourcePath", path ) );
} else if ( !path.equals( "/" ) && path.endsWith( "/" ) ) {
path = path.substring( 0, path.length( ) - 1 );
}
File res = new File( webRoot, path.substring( 1 ) );
return ( res != null ) && res.exists( ) ? res.toURL( ) : null;
}
1815 public InputStream getResourceAsStream( String path ) {
try {
URL res = getResource( path );
return res == null ? null : res.openStream( );
} catch ( IOException err ) {
throw new WinstoneException( Launcher.RESOURCES
.getString( "WebAppConfig.ErrorOpeningStream" ), err );
}
}
1825 public String getRealPath( String path ) {
// Trim the prefix
if ( path == null )
return null;
else {
try {
File res = new File( this.webRoot, path );
if ( res.isDirectory( ) )
return res.getCanonicalPath( ) + "/";
else
return res.getCanonicalPath( );
} catch ( IOException err ) {
return null;
}
}
}
1842 public Set getResourcePaths( String path ) {
// Trim the prefix
if ( path == null )
return null;
else if ( !path.startsWith( "/" ) )
throw new WinstoneException( Launcher.RESOURCES.getString(
"WebAppConfig.BadResourcePath", path ) );
else {
String workingPath = null;
if ( path.equals( "/" ) )
workingPath = "";
else {
boolean lastCharIsSlash = path.charAt( path.length( ) - 1 ) == '/';
workingPath = path.substring( 1, path.length( )
- ( lastCharIsSlash ? 1 : 0 ) );
}
File inPath = new File( this.webRoot, workingPath.equals( "" ) ? "."
: workingPath ).getAbsoluteFile( );
if ( !inPath.exists( ) )
return null;
else if ( !inPath.isDirectory( ) )
return null;
// Find all the files in this folder
File children[] = inPath.listFiles( );
Set out = new HashSet( );
for ( int n = 0; n < children.length; n++ ) {
// Write the entry as subpath + child element
String entry = //this.prefix +
"/" + ( workingPath.length( ) != 0 ? workingPath + "/" : "" )
+ children[n].getName( )
+ ( children[n].isDirectory( ) ? "/" : "" );
out.add( entry );
}
return out;
}
}
/**
* @deprecated
*/
1883 public javax.servlet.Servlet getServlet( String name ) {
return null;
}
/**
* @deprecated
*/
1890 public Enumeration getServletNames( ) {
return Collections.enumeration( new ArrayList( ) );
}
/**
* @deprecated
*/
1897 public Enumeration getServlets( ) {
return Collections.enumeration( new ArrayList( ) );
}
/**
* @deprecated
*/
1904 public void log( Exception exception, String msg ) {
this.log( msg, exception );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* The web.xml parsing logic. This is used by more than one launcher, so it's shared from here.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WebXmlParser.java, v 1.9 2006/12/08 04:08:44 rickknowles Exp $
*/
30 public class WebXmlParser implements EntityResolver, ErrorHandler {
32 private ClassLoader commonLoader;
private boolean rethrowValidationExceptions;
35 public WebXmlParser( ClassLoader commonCL ) {
this.commonLoader = commonCL;
this.rethrowValidationExceptions = true;
}
40 private final static String SCHEMA_SOURCE_PROPERTY = "http://java.sun.com/xml/jaxp/properties/schemaSource";
/**
* Get a parsed XML DOM from the given inputstream. Used to process the
* web.xml application deployment descriptors. Returns null if the parse fails,
* so the effect is as if there was no web.xml file available.
*/
47 protected Document parseStreamToXML( File webXmlFile ) {
DocumentBuilderFactory factory = getBaseDBF( );
URL localXSD25 = this.commonLoader.getResource( LOCAL_ENTITY_TABLE[3][2] );
URL localXSD24 = this.commonLoader.getResource( LOCAL_ENTITY_TABLE[2][2] );
// Test for XSD compliance
try {
factory.setAttribute( "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema" );
if ( localXSD25 != null ) {
factory.setAttribute( SCHEMA_SOURCE_PROPERTY, localXSD25.toString( ) );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WebXmlParser.Local25XSDEnabled" );
} else if ( localXSD24 != null ) {
factory.setAttribute( SCHEMA_SOURCE_PROPERTY, localXSD24.toString( ) );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WebXmlParser.Local24XSDEnabled" );
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WebXmlParser.2524XSDNotFound" );
}
} catch ( Throwable err ) {
// if non-compliant parser, then parse as non-XSD compliant
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WebXmlParser.NonXSDParser" );
try {
this.rethrowValidationExceptions = false;
return parseAsV23Webapp( webXmlFile );
} catch ( Throwable v23Err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebXmlParser.WebXML23ParseError", v23Err );
return null;
}
}
// XSD compliant parser available, so parse as 2.5
try {
if ( localXSD25 != null ) {
factory.setAttribute( SCHEMA_SOURCE_PROPERTY, localXSD25.toString( ) );
} else {
factory.setAttribute( SCHEMA_SOURCE_PROPERTY, null );
}
DocumentBuilder builder = factory.newDocumentBuilder( );
builder.setEntityResolver( this );
builder.setErrorHandler( this );
this.rethrowValidationExceptions = true;
return builder.parse( webXmlFile );
} catch ( Throwable errV25 ) {
try {
// Try as 2.4
if ( localXSD24 != null ) {
factory.setAttribute( SCHEMA_SOURCE_PROPERTY, localXSD24.toString( ) );
} else {
factory.setAttribute( SCHEMA_SOURCE_PROPERTY, null );
}
DocumentBuilder builder = factory.newDocumentBuilder( );
builder.setEntityResolver( this );
builder.setErrorHandler( this );
this.rethrowValidationExceptions = true;
return builder.parse( webXmlFile );
} catch ( Throwable errV24 ) {
// Try parsing as a v2.3 spec webapp, and if another error happens, report 2.3, 2.4, 2.5
try {
this.rethrowValidationExceptions = false;
return parseAsV23Webapp( webXmlFile );
} catch ( Throwable errV23 ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebXmlParser.WebXMLBothErrors" );
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebXmlParser.WebXML25ParseError", errV25 );
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebXmlParser.WebXML24ParseError", errV24 );
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WebXmlParser.WebXML23ParseError", errV23 );
return null;
}
}
}
}
119 private Document parseAsV23Webapp( File webXmlFile ) throws ParserConfigurationException,
SAXException, IOException {
DocumentBuilderFactory factory = getBaseDBF( );
DocumentBuilder builder = factory.newDocumentBuilder( );
builder.setEntityResolver( this );
builder.setErrorHandler( this );
return builder.parse( webXmlFile );
}
128 private DocumentBuilderFactory getBaseDBF( ) {
// Use JAXP to create a document builder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance( );
factory.setExpandEntityReferences( false );
factory.setValidating( true );
factory.setNamespaceAware( true );
factory.setIgnoringComments( true );
factory.setCoalescing( true );
factory.setIgnoringElementContentWhitespace( true );
return factory;
}
/**
* Table mapping public doctypes and system ids against local classloader paths. This
* is used to resolve local entities where possible.
* Column 0 = public doctype
* Column 1 = system id
* Column 2 = local path
*/
147 private static final String LOCAL_ENTITY_TABLE[][] = {
{"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN", null, "javax/servlet/resources/web-app_2_2.dtd"},
{"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN", null, "javax/servlet/resources/web-app_2_3.dtd"},
{null, "http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd", "javax/servlet/resources/web-app_2_4.xsd"},
{null, "http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd", "javax/servlet/resources/web-app_2_5.xsd"},
{null, "http://www.w3.org/2001/xml.xsd", "javax/servlet/resources/xml.xsd"},
{"-//W3C//DTD XMLSCHEMA 200102//EN", null, "javax/servlet/resources/XMLSchema.dtd"},
{null, "http://www.w3.org/2001/datatypes.dtd", "javax/servlet/resources/datatypes.dtd"},
{null, "http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd", "javax/servlet/resources/j2ee_1_4.xsd"},
{null, "http://java.sun.com/xml/ns/j2ee/javaee_5.xsd", "javax/servlet/resources/javaee_5.xsd"},
{null, "http://java.sun.com/xml/ns/j2ee/jsp_2_0.xsd", "javax/servlet/resources/jsp_2_0.xsd"},
{null, "http://java.sun.com/xml/ns/j2ee/jsp_2_1.xsd", "javax/servlet/resources/jsp_2_1.xsd"},
{null, "http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd", "javax/servlet/resources/j2ee_web_services_client_1_1.xsd"},
{null, "http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_2.xsd", "javax/servlet/resources/javaee_web_services_client_1_2.xsd"}
};
/**
* Implements the EntityResolver interface. This allows us to redirect any
* requests by the parser for webapp DTDs to local copies. It's faster and
* it means you can run winstone without being web-connected.
*/
168 public InputSource resolveEntity( String publicName, String url )
throws SAXException, IOException {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WebXmlParser.ResolvingEntity",
new String[] { publicName, url } );
for ( int n = 0; n < LOCAL_ENTITY_TABLE.length; n++ ) {
if ( ( ( LOCAL_ENTITY_TABLE[n][0] != null ) && ( publicName != null ) &&
publicName.equals( LOCAL_ENTITY_TABLE[n][0] ) ) ||
( ( LOCAL_ENTITY_TABLE[n][1] != null ) && ( url != null ) &&
url.equals( LOCAL_ENTITY_TABLE[n][1] ) ) ) {
if ( this.commonLoader.getResource( LOCAL_ENTITY_TABLE[n][2] ) != null ) {
return getLocalResource( url, LOCAL_ENTITY_TABLE[n][2] );
}
}
}
if ( ( url != null ) && url.startsWith( "jar:" ) ) {
return getLocalResource( url, url.substring( url.indexOf( "!/" ) + 2 ) );
} else if ( ( url != null ) && url.startsWith( "file:" ) ) {
return new InputSource( url );
} else {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WebXmlParser.NoLocalResource", url );
return new InputSource( url );
}
}
193 private InputSource getLocalResource( String url, String local ) {
if ( this.commonLoader.getResource( local ) == null )
return new InputSource( url );
InputSource is = new InputSource( this.commonLoader.getResourceAsStream( local ) );
is.setSystemId( url );
return is;
}
201 public void error( SAXParseException exception ) throws SAXException {
if ( this.rethrowValidationExceptions ) {
throw exception;
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WebXmlParser.XMLParseError",
new String[] { exception.getLineNumber( ) + "",
exception.getMessage( ) } );
}
}
211 public void fatalError( SAXParseException exception ) throws SAXException {
error( exception );
}
215 public void warning( SAXParseException exception ) throws SAXException {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WebXmlParser.XMLParseError",
new String[] { exception.getLineNumber( ) + "",
exception.getMessage( ) } );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* Master exception within the servlet container. This is thrown whenever a
* non-recoverable error occurs that we want to throw to the top of the
* application.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneException.java, v 1.1 2004/03/08 15:27:21 rickknowles
* Exp $
*/
21 public class WinstoneException extends RuntimeException {
22 private Throwable nestedError = null;
/**
* Create an exception with a useful message for the system administrator.
*
* @param pMsg
* Error message for to be used for administrative
* troubleshooting
*/
31 public WinstoneException( String pMsg ) {
super( pMsg );
}
/**
* Create an exception with a useful message for the system administrator
* and a nested throwable object.
*
* @param pMsg
* Error message for administrative troubleshooting
* @param pError
* The actual exception that occurred
*/
44 public WinstoneException( String pMsg, Throwable pError ) {
super( pMsg );
this.setNestedError( pError );
}
/**
* Get the nested error or exception
*
* @return The nested error or exception
*/
54 public Throwable getNestedError( ) {
return this.nestedError;
}
/**
* Set the nested error or exception
*
* @param pError
* The nested error or exception
*/
64 private void setNestedError( Throwable pError ) {
this.nestedError = pError;
}
68 public void printStackTrace( PrintWriter p ) {
if ( this.nestedError != null )
this.nestedError.printStackTrace( p );
p.write( "\n" );
super.printStackTrace( p );
}
75 public void printStackTrace( PrintStream p ) {
if ( this.nestedError != null )
this.nestedError.printStackTrace( p );
p.println( "\n" );
super.printStackTrace( p );
}
82 public void printStackTrace( ) {
if ( this.nestedError != null )
this.nestedError.printStackTrace( );
super.printStackTrace( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* The request stream management class.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneInputStream.java, v 1.4 2006/02/28 07:32:47 rickknowles Exp $
*/
20 public class WinstoneInputStream extends javax.servlet.ServletInputStream {
final int BUFFER_SIZE = 4096;
22 private InputStream inData;
23 private Integer contentLength;
private int readSoFar;
25 private ByteArrayOutputStream dump;
/**
* Constructor
*/
30 public WinstoneInputStream( InputStream inData ) {
super( );
this.inData = inData;
this.dump = new ByteArrayOutputStream( );
}
36 public WinstoneInputStream( byte inData[] ) {
this( new ByteArrayInputStream( inData ) );
}
40 public InputStream getRawInputStream( ) {
return this.inData;
}
44 public void setContentLength( int length ) {
this.contentLength = new Integer( length );
this.readSoFar = 0;
}
49 public int read( ) throws IOException {
if ( this.contentLength == null ) {
int data = this.inData.read( );
this.dump.write( data );
// System.out.println( "Char: " + ( char ) data );
return data;
} else if ( this.contentLength.intValue( ) > this.readSoFar ) {
this.readSoFar++;
int data = this.inData.read( );
this.dump.write( data );
// System.out.println( "Char: " + ( char ) data );
return data;
} else
return -1;
}
65 public void finishRequest( ) {
// this.inData = null;
// byte content[] = this.dump.toByteArray( );
// com.rickknowles.winstone.ajp13.Ajp13Listener.packetDump( content,
// content.length );
}
72 public int available( ) throws IOException {
return this.inData.available( );
}
/**
* Wrapper for the servletInputStream's readline method
*/
79 public byte[] readLine( ) throws IOException {
// System.out.println( "ReadLine( )" );
byte buffer[] = new byte[BUFFER_SIZE];
int charsRead = super.readLine( buffer, 0, BUFFER_SIZE );
if ( charsRead == -1 ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WinstoneInputStream.EndOfStream" );
return new byte[0];
}
byte outBuf[] = new byte[charsRead];
System.arraycopy( buffer, 0, outBuf, 0, charsRead );
return outBuf;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Stack;
import javax.servlet.http.Cookie;
/**
* Matches the socket output stream to the servlet output.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneOutputStream.java, v 1.19 2007/10/14 14:48:14 rickknowles Exp $
*/
23 public class WinstoneOutputStream extends javax.servlet.ServletOutputStream {
private static final int DEFAULT_BUFFER_SIZE = 8192;
private static final byte[] CR_LF = "\r\n".getBytes( );
26 protected OutputStream outStream;
protected int bufferSize;
protected int bufferPosition;
protected int bytesCommitted;
30 protected ByteArrayOutputStream buffer;
protected boolean committed;
protected boolean bodyOnly;
33 protected WinstoneResponse owner;
protected boolean disregardMode = false;
protected boolean closed = false;
36 protected Stack includeByteStreams;
/**
* Constructor
*/
41 public WinstoneOutputStream( OutputStream out, boolean bodyOnlyForInclude ) {
this.outStream = out;
this.bodyOnly = bodyOnlyForInclude;
this.bufferSize = DEFAULT_BUFFER_SIZE;
this.committed = false;
// this.headersWritten = false;
this.buffer = new ByteArrayOutputStream( );
}
50 public void setResponse( WinstoneResponse response ) {
this.owner = response;
}
54 public int getBufferSize( ) {
return this.bufferSize;
}
58 public void setBufferSize( int bufferSize ) {
if ( this.owner.isCommitted( ) ) {
throw new IllegalStateException( Launcher.RESOURCES.getString(
"WinstoneOutputStream.AlreadyCommitted" ) );
}
this.bufferSize = bufferSize;
}
66 public boolean isCommitted( ) {
return this.committed;
}
70 public int getOutputStreamLength( ) {
return this.bytesCommitted + this.bufferPosition;
}
74 public int getBytesCommitted( ) {
return this.bytesCommitted;
}
78 public void setDisregardMode( boolean disregard ) {
this.disregardMode = disregard;
}
82 public void setClosed( boolean closed ) {
this.closed = closed;
}
86 public void write( int oneChar ) throws IOException {
if ( this.disregardMode || this.closed ) {
return;
}
String contentLengthHeader = this.owner.getHeader( WinstoneResponse.CONTENT_LENGTH_HEADER );
if ( ( contentLengthHeader != null ) &&
( this.bytesCommitted >= Integer.parseInt( contentLengthHeader ) ) ) {
return;
}
// System.out.println( "Out: " + this.bufferPosition + " char=" + ( char )oneChar );
this.buffer.write( oneChar );
this.bufferPosition++;
// if ( this.headersWritten )
if ( this.bufferPosition >= this.bufferSize ) {
commit( );
} else if ( ( contentLengthHeader != null ) &&
( ( this.bufferPosition + this.bytesCommitted )
>= Integer.parseInt( contentLengthHeader ) ) ) {
commit( );
}
}
108 public void commit( ) throws IOException {
this.buffer.flush( );
// If we haven't written the headers yet, write them out
if ( !this.committed && !this.bodyOnly ) {
this.owner.validateHeaders( );
this.committed = true;
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneOutputStream.CommittingOutputStream" );
int statusCode = this.owner.getStatus( );
String reason = Launcher.RESOURCES.getString( "WinstoneOutputStream.reasonPhrase." + statusCode );
String statusLine = this.owner.getProtocol( ) + " " + statusCode + " " +
( reason == null ? "No reason" : reason );
this.outStream.write( statusLine.getBytes( "8859_1" ) );
this.outStream.write( CR_LF );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneOutputStream.ResponseStatus", statusLine );
// Write headers and cookies
for ( Iterator i = this.owner.getHeaders( ).iterator( ); i.hasNext( ); ) {
String header = ( String ) i.next( );
this.outStream.write( header.getBytes( "8859_1" ) );
this.outStream.write( CR_LF );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneOutputStream.Header", header );
}
if ( !this.owner.getHeaders( ).isEmpty( ) ) {
for ( Iterator i = this.owner.getCookies( ).iterator( ); i.hasNext( ); ) {
Cookie cookie = ( Cookie ) i.next( );
String cookieText = this.owner.writeCookie( cookie );
this.outStream.write( cookieText.getBytes( "8859_1" ) );
this.outStream.write( CR_LF );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneOutputStream.Header", cookieText );
}
}
this.outStream.write( CR_LF );
this.outStream.flush( );
// Logger.log( Logger.FULL_DEBUG,
// Launcher.RESOURCES.getString( "HttpProtocol.OutHeaders" ) + out.toString( ) );
}
byte content[] = this.buffer.toByteArray( );
// winstone.ajp13.Ajp13Listener.packetDump( content, content.length );
// this.buffer.writeTo( this.outStream );
int commitLength = content.length;
String contentLengthHeader = this.owner.getHeader( WinstoneResponse.CONTENT_LENGTH_HEADER );
if ( contentLengthHeader != null ) {
commitLength = Math.min( Integer.parseInt( contentLengthHeader )
- this.bytesCommitted, content.length );
}
if ( commitLength > 0 ) {
this.outStream.write( content, 0, commitLength );
}
this.outStream.flush( );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneOutputStream.CommittedBytes",
"" + ( this.bytesCommitted + commitLength ) );
this.bytesCommitted += commitLength;
this.buffer.reset( );
this.bufferPosition = 0;
}
174 public void reset( ) {
if ( isCommitted( ) )
throw new IllegalStateException( Launcher.RESOURCES
.getString( "WinstoneOutputStream.AlreadyCommitted" ) );
else {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneOutputStream.ResetBuffer", this.bufferPosition
+ "" );
this.buffer.reset( );
this.bufferPosition = 0;
this.bytesCommitted = 0;
}
}
188 public void finishResponse( ) throws IOException {
this.outStream.flush( );
this.outStream = null;
}
193 public void flush( ) throws IOException {
if ( this.disregardMode ) {
return;
}
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WinstoneOutputStream.Flushing" );
this.buffer.flush( );
this.commit( );
}
202 public void close( ) throws IOException {
if ( !isCommitted( ) && !this.disregardMode && !this.closed &&
( this.owner.getHeader( WinstoneResponse.CONTENT_LENGTH_HEADER ) == null ) ) {
if ( ( this.owner != null ) && !this.bodyOnly ) {
this.owner.setContentLength( getOutputStreamLength( ) );
}
}
flush( );
}
// Include related buffering
213 public boolean isIncluding( ) {
return ( this.includeByteStreams != null && !this.includeByteStreams.isEmpty( ) );
}
217 public void startIncludeBuffer( ) {
synchronized ( this.buffer ) {
if ( this.includeByteStreams == null ) {
this.includeByteStreams = new Stack( );
}
}
this.includeByteStreams.push( new ByteArrayOutputStream( ) );
}
226 public void finishIncludeBuffer( ) throws IOException {
if ( isIncluding( ) ) {
ByteArrayOutputStream body = ( ByteArrayOutputStream ) this.includeByteStreams.pop( );
OutputStream topStream = this.outStream;
if ( !this.includeByteStreams.isEmpty( ) ) {
topStream = ( OutputStream ) this.includeByteStreams.peek( );
}
byte bodyArr[] = body.toByteArray( );
if ( bodyArr.length > 0 ) {
topStream.write( bodyArr );
}
body.close( );
}
}
241 public void clearIncludeStackForForward( ) throws IOException {
if ( isIncluding( ) ) {
for ( Iterator i = this.includeByteStreams.iterator( ); i.hasNext( ); ) {
( ( ByteArrayOutputStream ) i.next( ) ).close( );
}
this.includeByteStreams.clear( );
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.TimeZone;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* Implements the request interface required by the servlet spec.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneRequest.java, v 1.38 2007/10/28 16:29:02 rickknowles Exp $
*/
54 public class WinstoneRequest implements HttpServletRequest {
55 protected static DateFormat headerDF = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss z", Locale.US );
57 protected static Random rnd = null;
static {
headerDF.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
rnd = new Random( System.currentTimeMillis( ) );
}
// Request header constants
64 static final String CONTENT_LENGTH_HEADER = "Content-Length";
65 static final String CONTENT_TYPE_HEADER = "Content-Type";
66 static final String AUTHORIZATION_HEADER = "Authorization";
67 static final String LOCALE_HEADER = "Accept-Language";
68 static final String HOST_HEADER = "Host";
69 static final String IN_COOKIE_HEADER1 = "Cookie";
70 static final String IN_COOKIE_HEADER2 = "Cookie2";
71 static final String METHOD_HEAD = "HEAD";
72 static final String METHOD_GET = "GET";
73 static final String METHOD_POST = "POST";
74 static final String POST_PARAMETERS = "application/x-www-form-urlencoded";
76 protected Map attributes;
77 protected Map parameters;
78 protected Stack attributesStack;
79 protected Stack parametersStack;
// protected Map forwardedParameters;
82 protected String headers[];
83 protected Cookie cookies[];
85 protected String method;
86 protected String scheme;
87 protected String serverName;
88 protected String requestURI;
89 protected String servletPath;
90 protected String pathInfo;
91 protected String queryString;
92 protected String protocol;
protected int contentLength;
94 protected String contentType;
95 protected String encoding;
protected int serverPort;
98 protected String remoteIP;
99 protected String remoteName;
protected int remotePort;
101 protected String localAddr;
102 protected String localName;
protected int localPort;
104 protected Boolean parsedParameters;
105 protected Map requestedSessionIds;
106 protected Map currentSessionIds;
107 protected String deadRequestedSessionId;
108 protected List locales;
109 protected String authorization;
protected boolean isSecure;
112 protected WinstoneInputStream inputData;
113 protected BufferedReader inputReader;
114 protected ServletConfiguration servletConfig;
115 protected WebAppConfiguration webappConfig;
116 protected HostGroup hostGroup;
118 protected AuthenticationPrincipal authenticatedUser;
119 protected ServletRequestAttributeListener requestAttributeListeners[];
120 protected ServletRequestListener requestListeners[];
122 private MessageDigest md5Digester;
124 private Set usedSessions;
/**
* InputStream factory method.
*/
129 public WinstoneRequest( ) throws IOException {
this.attributes = new Hashtable( );
this.parameters = new Hashtable( );
this.locales = new ArrayList( );
this.attributesStack = new Stack( );
this.parametersStack = new Stack( );
// this.forwardedParameters = new Hashtable( );
this.requestedSessionIds = new Hashtable( );
this.currentSessionIds = new Hashtable( );
this.usedSessions = new HashSet( );
this.contentLength = -1;
this.isSecure = false;
try {
this.md5Digester = MessageDigest.getInstance( "MD5" );
} catch ( NoSuchAlgorithmException err ) {
throw new WinstoneException(
"MD5 digester unavailable - what the ...?" );
}
}
/**
* Resets the request to be reused
*/
152 public void cleanUp( ) {
this.requestListeners = null;
this.requestAttributeListeners = null;
this.attributes.clear( );
this.parameters.clear( );
this.attributesStack.clear( );
this.parametersStack.clear( );
// this.forwardedParameters.clear( );
this.usedSessions.clear( );
this.headers = null;
this.cookies = null;
this.method = null;
this.scheme = null;
this.serverName = null;
this.requestURI = null;
this.servletPath = null;
this.pathInfo = null;
this.queryString = null;
this.protocol = null;
this.contentLength = -1;
this.contentType = null;
this.encoding = null;
this.inputData = null;
this.inputReader = null;
this.servletConfig = null;
this.webappConfig = null;
this.hostGroup = null;
this.serverPort = -1;
this.remoteIP = null;
this.remoteName = null;
this.remotePort = -1;
this.localAddr = null;
this.localName = null;
this.localPort = -1;
this.parsedParameters = null;
this.requestedSessionIds.clear( );
this.currentSessionIds.clear( );
this.deadRequestedSessionId = null;
this.locales.clear( );
this.authorization = null;
this.isSecure = false;
this.authenticatedUser = null;
}
/**
* Steps through the header array, searching for the first header matching
*/
199 private String extractFirstHeader( String name ) {
for ( int n = 0; n < this.headers.length; n++ ) {
if ( this.headers[n].toUpperCase( ).startsWith( name.toUpperCase( ) + ':' ) ) {
return this.headers[n].substring( name.length( ) + 1 ).trim( ); // 1 for colon
}
}
return null;
}
208 private Collection extractHeaderNameList( ) {
Collection headerNames = new HashSet( );
for ( int n = 0; n < this.headers.length; n++ ) {
String name = this.headers[n];
int colonPos = name.indexOf( ':' );
headerNames.add( name.substring( 0, colonPos ) );
}
return headerNames;
}
218 public Map getAttributes( ) {
return this.attributes;
}
222 public Map getParameters( ) {
return this.parameters;
}
//
// public Map getForwardedParameters( ) {
// return this.forwardedParameters;
// }
230 public Stack getAttributesStack( ) {
return this.attributesStack;
}
234 public Stack getParametersStack( ) {
return this.parametersStack;
}
238 public Map getCurrentSessionIds( ) {
return this.currentSessionIds;
}
242 public Map getRequestedSessionIds( ) {
return this.requestedSessionIds;
}
246 public String getDeadRequestedSessionId( ) {
return this.deadRequestedSessionId;
}
250 public HostGroup getHostGroup( ) {
return this.hostGroup;
}
254 public WebAppConfiguration getWebAppConfig( ) {
return this.webappConfig;
}
258 public ServletConfiguration getServletConfig( ) {
return this.servletConfig;
}
262 public String getEncoding( ) {
return this.encoding;
}
266 public Boolean getParsedParameters( ) {
return this.parsedParameters;
}
270 public List getListLocales( ) {
return this.locales;
}
274 public void setInputStream( WinstoneInputStream inputData ) {
this.inputData = inputData;
}
278 public void setHostGroup( HostGroup hostGroup ) {
this.hostGroup = hostGroup;
}
282 public void setWebAppConfig( WebAppConfiguration webappConfig ) {
this.webappConfig = webappConfig;
}
286 public void setServletConfig( ServletConfiguration servletConfig ) {
this.servletConfig = servletConfig;
}
290 public void setServerPort( int port ) {
this.serverPort = port;
}
294 public void setRemoteIP( String remoteIP ) {
this.remoteIP = remoteIP;
}
298 public void setRemoteName( String name ) {
this.remoteName = name;
}
302 public void setRemotePort( int port ) {
this.remotePort = port;
}
306 public void setLocalAddr( String ip ) {
this.localName = ip;
}
310 public void setLocalName( String name ) {
this.localName = name;
}
314 public void setLocalPort( int port ) {
this.localPort = port;
}
318 public void setMethod( String method ) {
this.method = method;
}
322 public void setIsSecure( boolean isSecure ) {
this.isSecure = isSecure;
}
326 public void setQueryString( String queryString ) {
this.queryString = queryString;
}
330 public void setServerName( String name ) {
this.serverName = name;
}
334 public void setRequestURI( String requestURI ) {
this.requestURI = requestURI;
}
338 public void setScheme( String scheme ) {
this.scheme = scheme;
}
342 public void setServletPath( String servletPath ) {
this.servletPath = servletPath;
}
346 public void setPathInfo( String pathInfo ) {
this.pathInfo = pathInfo;
}
350 public void setProtocol( String protocolString ) {
this.protocol = protocolString;
}
354 public void setRemoteUser( AuthenticationPrincipal user ) {
this.authenticatedUser = user;
}
358 public void setContentLength( int len ) {
this.contentLength = len;
}
362 public void setContentType( String type ) {
this.contentType = type;
}
366 public void setAuthorization( String auth ) {
this.authorization = auth;
}
370 public void setLocales( List locales ) {
this.locales = locales;
}
374 public void setCurrentSessionIds( Map currentSessionIds ) {
this.currentSessionIds = currentSessionIds;
}
378 public void setRequestedSessionIds( Map requestedSessionIds ) {
this.requestedSessionIds = requestedSessionIds;
}
382 public void setDeadRequestedSessionId( String deadRequestedSessionId ) {
this.deadRequestedSessionId = deadRequestedSessionId;
}
386 public void setEncoding( String encoding ) {
this.encoding = encoding;
}
390 public void setParsedParameters( Boolean parsed ) {
this.parsedParameters = parsed;
}
394 public void setRequestListeners( ServletRequestListener rl[] ) {
this.requestListeners = rl;
}
398 public void setRequestAttributeListeners(
399 ServletRequestAttributeListener ral[] ) {
this.requestAttributeListeners = ral;
}
/**
* Gets parameters from the url encoded parameter string
*/
406 public static void extractParameters( String urlEncodedParams,
407 String encoding, Map outputParams, boolean overwrite ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WinstoneRequest.ParsingParameters", new String[] {
urlEncodedParams, encoding } );
StringTokenizer st = new StringTokenizer( urlEncodedParams, "&", false );
Set overwrittenParamNames = null;
while ( st.hasMoreTokens( ) ) {
String token = st.nextToken( );
int equalPos = token.indexOf( '=' );
try {
String decodedNameDefault = decodeURLToken( equalPos == -1 ? token
: token.substring( 0, equalPos ) );
String decodedValueDefault = ( equalPos == -1 ? ""
: decodeURLToken( token.substring( equalPos + 1 ) ) );
String decodedName = ( encoding == null ? decodedNameDefault
: new String( decodedNameDefault.getBytes( "8859_1" ), encoding ) );
String decodedValue = ( encoding == null ? decodedValueDefault
: new String( decodedValueDefault.getBytes( "8859_1" ), encoding ) );
Object already = null;
if ( overwrite ) {
if ( overwrittenParamNames == null ) {
overwrittenParamNames = new HashSet( );
}
if ( !overwrittenParamNames.contains( decodedName ) ) {
overwrittenParamNames.add( decodedName );
outputParams.remove( decodedName );
}
}
already = outputParams.get( decodedName );
if ( already == null ) {
outputParams.put( decodedName, decodedValue );
} else if ( already instanceof String ) {
String pair[] = new String[2];
pair[0] = ( String ) already;
pair[1] = decodedValue;
outputParams.put( decodedName, pair );
} else if ( already instanceof String[] ) {
String alreadyArray[] = ( String[] ) already;
String oneMore[] = new String[alreadyArray.length + 1];
System.arraycopy( alreadyArray, 0, oneMore, 0,
alreadyArray.length );
oneMore[oneMore.length - 1] = decodedValue;
outputParams.put( decodedName, oneMore );
} else {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneRequest.UnknownParameterType",
decodedName + " = " + decodedValue.getClass( ) );
}
} catch ( UnsupportedEncodingException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WinstoneRequest.ErrorParameters", err );
}
}
}
/**
* For decoding the URL encoding used on query strings
*/
466 public static String decodeURLToken( String in ) {
StringBuffer workspace = new StringBuffer( );
for ( int n = 0; n < in.length( ); n++ ) {
char thisChar = in.charAt( n );
if ( thisChar == '+' )
workspace.append( ' ' );
else if ( thisChar == '%' ) {
String token = in.substring( Math.min( n + 1, in.length( ) ),
Math.min( n + 3, in.length( ) ) );
try {
int decoded = Integer.parseInt( token, 16 );
workspace.append( ( char ) decoded );
n += 2;
} catch ( RuntimeException err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WinstoneRequest.InvalidURLTokenChar", token );
workspace.append( thisChar );
}
} else
workspace.append( thisChar );
}
return workspace.toString( );
}
489 public void discardRequestBody( ) {
if ( getContentLength( ) > 0 ) {
try {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneResponse.ForceBodyParsing" );
// If body not parsed
if ( ( this.parsedParameters == null ) ||
( this.parsedParameters.equals( Boolean.FALSE ) ) ) {
// read full stream length
try {
InputStream in = getInputStream( );
byte buffer[] = new byte[2048];
while ( in.read( buffer ) != -1 );
} catch ( IllegalStateException err ) {
Reader in = getReader( );
char buffer[] = new char[2048];
while ( in.read( buffer ) != -1 );
}
}
} catch ( IOException err ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneResponse.ErrorForceBodyParsing", err );
}
}
}
/**
* This takes the parameters in the body of the request and puts them into
* the parameters map.
*/
517 public void parseRequestParameters( ) {
if ( ( parsedParameters != null ) && !parsedParameters.booleanValue( ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneRequest.BothMethods" );
this.parsedParameters = Boolean.TRUE;
} else if ( parsedParameters == null ) {
Map workingParameters = new HashMap( );
try {
// Parse query string from request
if ( ( method.equals( METHOD_GET ) || method.equals( METHOD_HEAD ) ||
method.equals( METHOD_POST ) )
&& ( this.queryString != null ) ) {
extractParameters( this.queryString, this.encoding, workingParameters, false );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneRequest.ParamLine", "" + workingParameters );
}
if ( method.equals( METHOD_POST ) && ( contentType != null )
&& ( contentType.equals( POST_PARAMETERS )
|| contentType.startsWith( POST_PARAMETERS + ";" ) ) ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneRequest.ParsingBodyParameters" );
// Parse params
byte paramBuffer[] = new byte[contentLength];
int readCount = this.inputData.read( paramBuffer );
if ( readCount != contentLength )
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneRequest.IncorrectContentLength",
new String[] { contentLength + "",
readCount + "" } );
String paramLine = ( this.encoding == null ? new String(
paramBuffer ) : new String( paramBuffer,
this.encoding ) );
extractParameters( paramLine.trim( ), this.encoding, workingParameters, false );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneRequest.ParamLine", "" + workingParameters );
}
this.parameters.putAll( workingParameters );
this.parsedParameters = Boolean.TRUE;
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WinstoneRequest.ErrorBodyParameters", err );
this.parsedParameters = null;
}
}
}
/**
* Go through the list of headers, and build the headers/cookies arrays for
* the request object.
*/
570 public void parseHeaders( List headerList ) {
// Iterate through headers
List outHeaderList = new ArrayList( );
List cookieList = new ArrayList( );
for ( Iterator i = headerList.iterator( ); i.hasNext( ); ) {
String header = ( String ) i.next( );
int colonPos = header.indexOf( ':' );
String name = header.substring( 0, colonPos );
String value = header.substring( colonPos + 1 ).trim( );
// Add it to out headers if it's not a cookie
outHeaderList.add( header );
// if ( !name.equalsIgnoreCase( IN_COOKIE_HEADER1 )
// && !name.equalsIgnoreCase( IN_COOKIE_HEADER2 ) )
if ( name.equalsIgnoreCase( AUTHORIZATION_HEADER ) )
this.authorization = value;
else if ( name.equalsIgnoreCase( LOCALE_HEADER ) )
this.locales = parseLocales( value );
else if ( name.equalsIgnoreCase( CONTENT_LENGTH_HEADER ) )
this.contentLength = Integer.parseInt( value );
else if ( name.equalsIgnoreCase( HOST_HEADER ) ) {
int nextColonPos = value.indexOf( ':' );
if ( ( nextColonPos == -1 ) || ( nextColonPos == value.length( ) - 1 ) ) {
this.serverName = value;
if ( this.scheme != null ) {
if ( this.scheme.equals( "http" ) ) {
this.serverPort = 80;
} else if ( this.scheme.equals( "https" ) ) {
this.serverPort = 443;
}
}
} else {
this.serverName = value.substring( 0, nextColonPos );
this.serverPort = Integer.parseInt( value.substring( nextColonPos + 1 ) );
}
}
else if ( name.equalsIgnoreCase( CONTENT_TYPE_HEADER ) ) {
this.contentType = value;
int semicolon = value.lastIndexOf( ';' );
if ( semicolon != -1 ) {
String encodingClause = value.substring( semicolon + 1 ).trim( );
if ( encodingClause.startsWith( "charset=" ) )
this.encoding = encodingClause.substring( 8 );
}
} else if ( name.equalsIgnoreCase( IN_COOKIE_HEADER1 )
|| name.equalsIgnoreCase( IN_COOKIE_HEADER2 ) )
parseCookieLine( value, cookieList );
}
this.headers = ( String[] ) outHeaderList.toArray( new String[0] );
if ( cookieList.isEmpty( ) ) {
this.cookies = null;
} else {
this.cookies = ( Cookie[] ) cookieList.toArray( new Cookie[0] );
}
}
627 private static String nextToken( StringTokenizer st ) {
if ( st.hasMoreTokens( ) ) {
return st.nextToken( );
} else {
return null;
}
}
635 private void parseCookieLine( String headerValue, List cookieList ) {
StringTokenizer st = new StringTokenizer( headerValue, ";", false );
int version = 0;
String cookieLine = nextToken( st );
// check cookie version flag
if ( ( cookieLine != null ) && cookieLine.startsWith( "$Version=" ) ) {
int equalPos = cookieLine.indexOf( '=' );
try {
version = Integer.parseInt( extractFromQuotes(
cookieLine.substring( equalPos + 1 ).trim( ) ) );
} catch ( NumberFormatException err ) {
version = 0;
}
cookieLine = nextToken( st );
}
// process remainder - parameters
while ( cookieLine != null ) {
cookieLine = cookieLine.trim( );
int equalPos = cookieLine.indexOf( '=' );
if ( equalPos == -1 ) {
// next token
cookieLine = nextToken( st );
} else {
String name = cookieLine.substring( 0, equalPos );
String value = extractFromQuotes( cookieLine.substring( equalPos + 1 ) );
Cookie thisCookie = new Cookie( name, value );
thisCookie.setVersion( version );
thisCookie.setSecure( isSecure( ) );
cookieList.add( thisCookie );
// check for path / domain / port
cookieLine = nextToken( st );
while ( ( cookieLine != null ) && cookieLine.trim( ).startsWith( "$" ) ) {
cookieLine = cookieLine.trim( );
equalPos = cookieLine.indexOf( '=' );
String attrValue = equalPos == -1 ? "" : cookieLine
.substring( equalPos + 1 ).trim( );
if ( cookieLine.startsWith( "$Path" ) ) {
thisCookie.setPath( extractFromQuotes( attrValue ) );
} else if ( cookieLine.startsWith( "$Domain" ) ) {
thisCookie.setDomain( extractFromQuotes( attrValue ) );
}
cookieLine = nextToken( st );
}
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneRequest.CookieFound", thisCookie.toString( ) );
if ( thisCookie.getName( ).equals( WinstoneSession.SESSION_COOKIE_NAME ) ) {
// Find a context that manages this key
HostConfiguration hostConfig = this.hostGroup.getHostByName( this.serverName );
WebAppConfiguration ownerContext = hostConfig.getWebAppBySessionKey( thisCookie.getValue( ) );
if ( ownerContext != null ) {
this.requestedSessionIds.put( ownerContext.getContextPath( ),
thisCookie.getValue( ) );
this.currentSessionIds.put( ownerContext.getContextPath( ),
thisCookie.getValue( ) );
}
// If not found, it was probably dead
else {
this.deadRequestedSessionId = thisCookie.getValue( );
}
// this.requestedSessionId = thisCookie.getValue( );
// this.currentSessionId = thisCookie.getValue( );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneRequest.SessionCookieFound",
new String[] {thisCookie.getValue( ),
ownerContext == null ? "" : "prefix:" + ownerContext.getContextPath( )} );
}
}
}
}
709 private static String extractFromQuotes( String input ) {
if ( ( input != null ) && input.startsWith( "\"" ) && input.endsWith( "\"" ) ) {
return input.substring( 1, input.length( ) - 1 );
} else {
return input;
}
}
717 private List parseLocales( String header ) {
// Strip out the whitespace
StringBuffer lb = new StringBuffer( );
for ( int n = 0; n < header.length( ); n++ ) {
char c = header.charAt( n );
if ( !Character.isWhitespace( c ) )
lb.append( c );
}
// Tokenize by commas
Map localeEntries = new HashMap( );
StringTokenizer commaTK = new StringTokenizer( lb.toString( ), ", ", false );
for ( ; commaTK.hasMoreTokens( ); ) {
String clause = commaTK.nextToken( );
// Tokenize by semicolon
Float quality = new Float( 1 );
if ( clause.indexOf( ";q=" ) != -1 ) {
int pos = clause.indexOf( ";q=" );
try {
quality = new Float( clause.substring( pos + 3 ) );
} catch ( NumberFormatException err ) {
quality = new Float( 0 );
}
clause = clause.substring( 0, pos );
}
// Build the locale
String language = "";
String country = "";
String variant = "";
int dpos = clause.indexOf( '-' );
if ( dpos == -1 )
language = clause;
else {
language = clause.substring( 0, dpos );
String remainder = clause.substring( dpos + 1 );
int d2pos = remainder.indexOf( '-' );
if ( d2pos == -1 )
country = remainder;
else {
country = remainder.substring( 0, d2pos );
variant = remainder.substring( d2pos + 1 );
}
}
Locale loc = new Locale( language, country, variant );
// Put into list by quality
List localeList = ( List ) localeEntries.get( quality );
if ( localeList == null ) {
localeList = new ArrayList( );
localeEntries.put( quality, localeList );
}
localeList.add( loc );
}
// Extract and build the list
Float orderKeys[] = ( Float[] ) localeEntries.keySet( ).toArray( new Float[0] );
Arrays.sort( orderKeys );
List outputLocaleList = new ArrayList( );
for ( int n = 0; n < orderKeys.length; n++ ) {
// Skip backwards through the list of maps and add to the output list
int reversedIndex = ( orderKeys.length - 1 ) - n;
if ( ( orderKeys[reversedIndex].floatValue( ) <= 0 )
|| ( orderKeys[reversedIndex].floatValue( ) > 1 ) )
continue;
List localeList = ( List ) localeEntries.get( orderKeys[reversedIndex] );
for ( Iterator i = localeList.iterator( ); i.hasNext( ); )
outputLocaleList.add( i.next( ) );
}
return outputLocaleList;
}
791 public void addIncludeQueryParameters( String queryString ) {
Map lastParams = new Hashtable( );
if ( !this.parametersStack.isEmpty( ) ) {
lastParams.putAll( ( Map ) this.parametersStack.peek( ) );
}
Map newQueryParams = new HashMap( );
if ( queryString != null ) {
extractParameters( queryString, this.encoding, newQueryParams, false );
}
lastParams.putAll( newQueryParams );
this.parametersStack.push( lastParams );
}
804 public void addIncludeAttributes( String requestURI, String contextPath,
805 String servletPath, String pathInfo, String queryString ) {
Map includeAttributes = new HashMap( );
if ( requestURI != null ) {
includeAttributes.put( RequestDispatcher.INCLUDE_REQUEST_URI, requestURI );
}
if ( contextPath != null ) {
includeAttributes.put( RequestDispatcher.INCLUDE_CONTEXT_PATH, contextPath );
}
if ( servletPath != null ) {
includeAttributes.put( RequestDispatcher.INCLUDE_SERVLET_PATH, servletPath );
}
if ( pathInfo != null ) {
includeAttributes.put( RequestDispatcher.INCLUDE_PATH_INFO, pathInfo );
}
if ( queryString != null ) {
includeAttributes.put( RequestDispatcher.INCLUDE_QUERY_STRING, queryString );
}
this.attributesStack.push( includeAttributes );
}
825 public void removeIncludeQueryString( ) {
if ( !this.parametersStack.isEmpty( ) ) {
this.parametersStack.pop( );
}
}
831 public void clearIncludeStackForForward( ) {
this.parametersStack.clear( );
this.attributesStack.clear( );
}
836 public void setForwardQueryString( String forwardQueryString ) {
// this.forwardedParameters.clear( );
// Parse query string from include / forward
if ( forwardQueryString != null ) {
String oldQueryString = this.queryString == null ? "" : this.queryString;
boolean needJoiner = !forwardQueryString.equals( "" ) && !oldQueryString.equals( "" );
this.queryString = forwardQueryString + ( needJoiner ? "&" : "" ) + oldQueryString;
if ( this.parsedParameters != null ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneRequest.ParsingParameters", new String[] {
forwardQueryString, this.encoding } );
extractParameters( forwardQueryString, this.encoding, this.parameters, true );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneRequest.ParamLine", "" + this.parameters );
}
}
}
857 public void removeIncludeAttributes( ) {
if ( !this.attributesStack.isEmpty( ) ) {
this.attributesStack.pop( );
}
}
// Implementation methods for the servlet request stuff
864 public Object getAttribute( String name ) {
if ( !this.attributesStack.isEmpty( ) ) {
Map includedAttributes = ( Map ) this.attributesStack.peek( );
Object value = includedAttributes.get( name );
if ( value != null ) {
return value;
}
}
return this.attributes.get( name );
}
875 public Enumeration getAttributeNames( ) {
Map attributes = new HashMap( this.attributes );
if ( !this.attributesStack.isEmpty( ) ) {
Map includedAttributes = ( Map ) this.attributesStack.peek( );
attributes.putAll( includedAttributes );
}
return Collections.enumeration( attributes.keySet( ) );
}
884 public void removeAttribute( String name ) {
Object value = attributes.get( name );
if ( value == null )
return;
// fire event
if ( this.requestAttributeListeners != null ) {
for ( int n = 0; n < this.requestAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( getWebAppConfig( ).getLoader( ) );
this.requestAttributeListeners[n].attributeRemoved(
new ServletRequestAttributeEvent( this.webappConfig,
this, name, value ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
this.attributes.remove( name );
}
904 public void setAttribute( String name, Object o ) {
if ( ( name != null ) && ( o != null ) ) {
Object oldValue = attributes.get( name );
attributes.put( name, o ); // make sure it's set at the top level
// fire event
if ( this.requestAttributeListeners != null ) {
if ( oldValue == null ) {
for ( int n = 0; n < this.requestAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( getWebAppConfig( ).getLoader( ) );
this.requestAttributeListeners[n].attributeAdded(
new ServletRequestAttributeEvent( this.webappConfig,
this, name, o ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
} else {
for ( int n = 0; n < this.requestAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( getWebAppConfig( ).getLoader( ) );
this.requestAttributeListeners[n]
.attributeReplaced( new ServletRequestAttributeEvent(
this.webappConfig, this, name, oldValue ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
}
} else if ( name != null ) {
removeAttribute( name );
}
}
936 public String getCharacterEncoding( ) {
return this.encoding;
}
940 public void setCharacterEncoding( String encoding ) throws UnsupportedEncodingException {
"blah".getBytes( encoding ); // throws an exception if the encoding is unsupported
if ( this.inputReader == null ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneRequest.SetCharEncoding",
new String[] { this.encoding, encoding } );
this.encoding = encoding;
}
}
949 public int getContentLength( ) {
return this.contentLength;
}
953 public String getContentType( ) {
return this.contentType;
}
957 public Locale getLocale( ) {
return this.locales.isEmpty( ) ? Locale.getDefault( )
: ( Locale ) this.locales.get( 0 );
}
962 public Enumeration getLocales( ) {
List sendLocales = this.locales;
if ( sendLocales.isEmpty( ) )
sendLocales.add( Locale.getDefault( ) );
return Collections.enumeration( sendLocales );
}
969 public String getProtocol( ) {
return this.protocol;
}
973 public String getScheme( ) {
return this.scheme;
}
977 public boolean isSecure( ) {
return this.isSecure;
}
981 public BufferedReader getReader( ) throws IOException {
if ( this.inputReader != null ) {
return this.inputReader;
} else {
if ( this.parsedParameters != null ) {
if ( this.parsedParameters.equals( Boolean.TRUE ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WinstoneRequest.BothMethodsReader" );
} else {
throw new IllegalStateException( Launcher.RESOURCES.getString(
"WinstoneRequest.CalledReaderAfterStream" ) );
}
}
if ( this.encoding != null ) {
this.inputReader = new BufferedReader( new InputStreamReader(
this.inputData, this.encoding ) );
} else {
this.inputReader = new BufferedReader( new InputStreamReader(
this.inputData ) );
}
this.parsedParameters = new Boolean( false );
return this.inputReader;
}
}
1005 public ServletInputStream getInputStream( ) throws IOException {
if ( this.inputReader != null ) {
throw new IllegalStateException( Launcher.RESOURCES.getString(
"WinstoneRequest.CalledStreamAfterReader" ) );
}
if ( this.parsedParameters != null ) {
if ( this.parsedParameters.equals( Boolean.TRUE ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WinstoneRequest.BothMethods" );
}
}
this.parsedParameters = new Boolean( false );
return this.inputData;
}
1019 public String getParameter( String name ) {
parseRequestParameters( );
Object param = null;
if ( !this.parametersStack.isEmpty( ) ) {
param = ( ( Map ) this.parametersStack.peek( ) ).get( name );
}
// if ( ( param == null ) && this.forwardedParameters.get( name ) != null ) {
// param = this.forwardedParameters.get( name );
// }
if ( param == null ) {
param = this.parameters.get( name );
}
if ( param == null )
return null;
else if ( param instanceof String )
return ( String ) param;
else if ( param instanceof String[] )
return ( ( String[] ) param )[0];
else
return param.toString( );
}
1041 public Enumeration getParameterNames( ) {
parseRequestParameters( );
Set parameterKeys = new HashSet( this.parameters.keySet( ) );
// parameterKeys.addAll( this.forwardedParameters.keySet( ) );
if ( !this.parametersStack.isEmpty( ) ) {
parameterKeys.addAll( ( ( Map ) this.parametersStack.peek( ) ).keySet( ) );
}
return Collections.enumeration( parameterKeys );
}
1051 public String[] getParameterValues( String name ) {
parseRequestParameters( );
Object param = null;
if ( !this.parametersStack.isEmpty( ) ) {
param = ( ( Map ) this.parametersStack.peek( ) ).get( name );
}
// if ( ( param == null ) && this.forwardedParameters.get( name ) != null ) {
// param = this.forwardedParameters.get( name );
// }
if ( param == null ) {
param = this.parameters.get( name );
}
if ( param == null )
return null;
else if ( param instanceof String ) {
return new String[] {( String ) param};
} else if ( param instanceof String[] )
return ( String[] ) param;
else
throw new WinstoneException( Launcher.RESOURCES.getString(
"WinstoneRequest.UnknownParameterType", name + " - "
+ param.getClass( ) ) );
}
1075 public Map getParameterMap( ) {
Hashtable paramMap = new Hashtable( );
for ( Enumeration names = this.getParameterNames( ); names
.hasMoreElements( ); ) {
String name = ( String ) names.nextElement( );
paramMap.put( name, getParameterValues( name ) );
}
return paramMap;
}
1085 public String getServerName( ) {
return this.serverName;
}
1089 public int getServerPort( ) {
return this.serverPort;
}
1093 public String getRemoteAddr( ) {
return this.remoteIP;
}
1097 public String getRemoteHost( ) {
return this.remoteName;
}
1101 public int getRemotePort( ) {
return this.remotePort;
}
1105 public String getLocalAddr( ) {
return this.localAddr;
}
1109 public String getLocalName( ) {
return this.localName;
}
1113 public int getLocalPort( ) {
return this.localPort;
}
1117 public javax.servlet.RequestDispatcher getRequestDispatcher( String path ) {
if ( path.startsWith( "/" ) )
return this.webappConfig.getRequestDispatcher( path );
// Take the servlet path + pathInfo, and make an absolute path
String fullPath = getServletPath( )
+ ( getPathInfo( ) == null ? "" : getPathInfo( ) );
int lastSlash = fullPath.lastIndexOf( '/' );
String currentDir = ( lastSlash == -1 ? "/" : fullPath.substring( 0,
lastSlash + 1 ) );
return this.webappConfig.getRequestDispatcher( currentDir + path );
}
// Now the stuff for HttpServletRequest
1131 public String getContextPath( ) {
return this.webappConfig.getContextPath( );
}
1135 public Cookie[] getCookies( ) {
return this.cookies;
}
1139 public long getDateHeader( String name ) {
String dateHeader = getHeader( name );
if ( dateHeader == null ) {
return -1;
} else try {
Date date = null;
synchronized ( headerDF ) {
date = headerDF.parse( dateHeader );
}
return date.getTime( );
} catch ( java.text.ParseException err ) {
throw new IllegalArgumentException( Launcher.RESOURCES.getString(
"WinstoneRequest.BadDate", dateHeader ) );
}
}
1155 public int getIntHeader( String name ) {
String header = getHeader( name );
return header == null ? -1 : Integer.parseInt( header );
}
1160 public String getHeader( String name ) {
return extractFirstHeader( name );
}
1164 public Enumeration getHeaderNames( ) {
return Collections.enumeration( extractHeaderNameList( ) );
}
1168 public Enumeration getHeaders( String name ) {
List headers = new ArrayList( );
for ( int n = 0; n < this.headers.length; n++ )
if ( this.headers[n].toUpperCase( ).startsWith(
name.toUpperCase( ) + ':' ) )
headers
.add( this.headers[n].substring( name.length( ) + 1 )
.trim( ) ); // 1 for colon
return Collections.enumeration( headers );
}
1179 public String getMethod( ) {
return this.method;
}
1183 public String getPathInfo( ) {
return this.pathInfo;
}
1187 public String getPathTranslated( ) {
return this.webappConfig.getRealPath( this.pathInfo );
}
1191 public String getQueryString( ) {
return this.queryString;
}
1195 public String getRequestURI( ) {
return this.requestURI;
}
1199 public String getServletPath( ) {
return this.servletPath;
}
1203 public String getRequestedSessionId( ) {
String actualSessionId = ( String ) this.requestedSessionIds.get( this.webappConfig.getContextPath( ) );
if ( actualSessionId != null ) {
return actualSessionId;
} else {
return this.deadRequestedSessionId;
}
}
1212 public StringBuffer getRequestURL( ) {
StringBuffer url = new StringBuffer( );
url.append( getScheme( ) ).append( "://" );
url.append( getServerName( ) );
if ( !( ( getServerPort( ) == 80 ) && getScheme( ).equals( "http" ) )
&& !( ( getServerPort( ) == 443 ) && getScheme( ).equals( "https" ) ) )
url.append( ':' ).append( getServerPort( ) );
url.append( getRequestURI( ) ); // need encoded form, so can't use servlet path + path info
return url;
}
1223 public Principal getUserPrincipal( ) {
return this.authenticatedUser;
}
1227 public boolean isUserInRole( String role ) {
if ( this.authenticatedUser == null )
return false;
else if ( this.servletConfig.getSecurityRoleRefs( ) == null )
return this.authenticatedUser.isUserIsInRole( role );
else {
String replacedRole = ( String ) this.servletConfig.getSecurityRoleRefs( ).get( role );
return this.authenticatedUser
.isUserIsInRole( replacedRole == null ? role : replacedRole );
}
}
1239 public String getAuthType( ) {
return this.authenticatedUser == null ? null : this.authenticatedUser
.getAuthType( );
}
1244 public String getRemoteUser( ) {
return this.authenticatedUser == null ? null : this.authenticatedUser
.getName( );
}
1249 public boolean isRequestedSessionIdFromCookie( ) {
return ( getRequestedSessionId( ) != null );
}
1253 public boolean isRequestedSessionIdFromURL( ) {
return false;
}
1257 public boolean isRequestedSessionIdValid( ) {
String requestedId = getRequestedSessionId( );
if ( requestedId == null ) {
return false;
}
WinstoneSession ws = this.webappConfig.getSessionById( requestedId, false );
return ( ws != null );
// if ( ws == null ) {
// return false;
// } else {
// return ( validationCheck( ws, System.currentTimeMillis( ), false ) != null );
// }
}
1271 public HttpSession getSession( ) {
return getSession( true );
}
1275 public HttpSession getSession( boolean create ) {
String cookieValue = ( String ) this.currentSessionIds.get( this.webappConfig.getContextPath( ) );
// Handle the null case
if ( cookieValue == null ) {
if ( !create ) {
return null;
} else {
cookieValue = makeNewSession( ).getId( );
}
}
// Now get the session object
WinstoneSession session = this.webappConfig.getSessionById( cookieValue, false );
if ( session != null ) {
// long nowDate = System.currentTimeMillis( );
// session = validationCheck( session, nowDate, create );
// if ( session == null ) {
// this.currentSessionIds.remove( this.webappConfig.getContextPath( ) );
// }
}
if ( create && ( session == null ) ) {
session = makeNewSession( );
}
if ( session != null ) {
this.usedSessions.add( session );
session.addUsed( this );
}
return session;
}
/**
* Make a new session, and return the id
*/
1309 private WinstoneSession makeNewSession( ) {
String cookieValue = "Winstone_" + this.remoteIP + "_"
+ this.serverPort + "_" + System.currentTimeMillis( ) + rnd.nextLong( );
byte digestBytes[] = this.md5Digester.digest( cookieValue.getBytes( ) );
// Write out in hex format
char outArray[] = new char[32];
for ( int n = 0; n < digestBytes.length; n++ ) {
int hiNibble = ( digestBytes[n] & 0xFF ) >> 4;
int loNibble = ( digestBytes[n] & 0xF );
outArray[2 * n] = ( hiNibble > 9 ? ( char ) ( hiNibble + 87 )
: ( char ) ( hiNibble + 48 ) );
outArray[2 * n + 1] = ( loNibble > 9 ? ( char ) ( loNibble + 87 )
: ( char ) ( loNibble + 48 ) );
}
String newSessionId = new String( outArray );
this.currentSessionIds.put( this.webappConfig.getContextPath( ), newSessionId );
return this.webappConfig.makeNewSession( newSessionId );
}
1330 public void markSessionsAsRequestFinished( long lastAccessedTime, boolean saveSessions ) {
for ( Iterator i = this.usedSessions.iterator( ); i.hasNext( ); ) {
WinstoneSession session = ( WinstoneSession ) i.next( );
session.setLastAccessedDate( lastAccessedTime );
session.removeUsed( this );
if ( saveSessions ) {
session.saveToTemp( );
}
}
this.usedSessions.clear( );
}
/**
* @deprecated
*/
1345 public String getRealPath( String path ) {
return this.webappConfig.getRealPath( path );
}
/**
* @deprecated
*/
1352 public boolean isRequestedSessionIdFromUrl( ) {
return isRequestedSessionIdFromURL( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* A ResourceBundle that includes the ability to do string replacement on the
* resources it retrieves.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
*/
18 public class WinstoneResourceBundle {
19 private ResourceBundle resources;
/**
* Constructor
*/
24 public WinstoneResourceBundle( String baseName ) {
this.resources = ResourceBundle.getBundle( baseName );
}
28 public WinstoneResourceBundle( String baseName, Locale loc ) {
this.resources = ResourceBundle.getBundle( baseName, loc );
}
32 public WinstoneResourceBundle( String baseName, Locale loc, ClassLoader cl ) {
this.resources = ResourceBundle.getBundle( baseName, loc, cl );
}
/**
* Default getString method
*/
39 public String getString( String key ) {
return this.resources.getString( key );
}
/**
* Perform a string replace for a single from/to pair.
*/
46 public String getString( String key, String parameter ) {
return globalReplace( this.resources.getString( key ), "[#0]", parameter );
}
/**
* Perform a string replace for a set of from/to pairs.
*/
53 public String getString( String key, String[] parameters ) {
String myCopy = this.resources.getString( key );
if ( parameters != null ) {
String tokens[][] = new String[parameters.length][2];
for ( int n = 0; n < parameters.length; n++ ) {
tokens[n] = new String[] {"[#" + n + "]", parameters[n]};
}
myCopy = globalReplace( myCopy, tokens );
}
return myCopy;
}
/**
* Just does a string swap, replacing occurrences of from with to.
*/
68 public static String globalReplace( String input, String fromMarker, String toValue ) {
StringBuffer out = new StringBuffer( input );
globalReplace( out, fromMarker, toValue );
return out.toString( );
}
74 private static void globalReplace( StringBuffer input, String fromMarker, String toValue ) {
if ( input == null ) {
return;
} else if ( fromMarker == null ) {
return;
}
int index = 0;
int foundAt = input.indexOf( fromMarker, index );
while ( foundAt != -1 ) {
if ( toValue == null ) {
toValue = "( null )";
}
input.replace( foundAt, foundAt + fromMarker.length( ), toValue );
index = foundAt + toValue.length( );
foundAt = input.indexOf( fromMarker, index );
}
}
93 public static String globalReplace( String input, String parameters[][] ) {
if ( parameters != null ) {
StringBuffer out = new StringBuffer( input );
for ( int n = 0; n < parameters.length; n++ ) {
globalReplace( out, parameters[n][0], parameters[n][1] );
}
return out.toString( );
} else {
return input;
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TimeZone;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
/**
* Response for servlet
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneResponse.java, v 1.28 2005/04/19 07:33:41 rickknowles
* Exp $
*/
35 public class WinstoneResponse implements HttpServletResponse {
36 private static final DateFormat HTTP_DF = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss z", Locale.US );
38 private static final DateFormat VERSION0_DF = new SimpleDateFormat(
"EEE, dd-MMM-yy HH:mm:ss z", Locale.US );
static {
HTTP_DF.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
VERSION0_DF.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
}
45 static final String CONTENT_LENGTH_HEADER = "Content-Length";
46 static final String CONTENT_TYPE_HEADER = "Content-Type";
// Response header constants
49 private static final String CONTENT_LANGUAGE_HEADER = "Content-Language";
50 private static final String KEEP_ALIVE_HEADER = "Connection";
51 private static final String KEEP_ALIVE_OPEN = "Keep-Alive";
52 private static final String KEEP_ALIVE_CLOSE = "Close";
53 private static final String DATE_HEADER = "Date";
54 private static final String LOCATION_HEADER = "Location";
55 private static final String OUT_COOKIE_HEADER1 = "Set-Cookie";
56 private static final String X_POWERED_BY_HEADER = "X-Powered-By";
57 private static final String X_POWERED_BY_HEADER_VALUE = Launcher.RESOURCES.getString( "PoweredByHeader" );
private int statusCode;
60 private WinstoneRequest req;
61 private WebAppConfiguration webAppConfig;
62 private WinstoneOutputStream outputStream;
63 private PrintWriter outputWriter;
65 private List headers;
66 private String explicitEncoding;
67 private String implicitEncoding;
68 private List cookies;
70 private Locale locale;
71 private String protocol;
72 private String reqKeepAliveHeader;
73 private Integer errorStatusCode;
/**
* Constructor
*/
78 public WinstoneResponse( ) {
this.headers = new ArrayList( );
this.cookies = new ArrayList( );
this.statusCode = SC_OK;
this.locale = null; //Locale.getDefault( );
this.explicitEncoding = null;
this.protocol = null;
this.reqKeepAliveHeader = null;
}
/**
* Resets the request to be reused
*/
93 public void cleanUp( ) {
this.req = null;
this.webAppConfig = null;
this.outputStream = null;
this.outputWriter = null;
this.headers.clear( );
this.cookies.clear( );
this.protocol = null;
this.reqKeepAliveHeader = null;
this.statusCode = SC_OK;
this.errorStatusCode = null;
this.locale = null; //Locale.getDefault( );
this.explicitEncoding = null;
this.implicitEncoding = null;
}
110 private String getEncodingFromLocale( Locale loc ) {
String localeString = loc.getLanguage( ) + "_" + loc.getCountry( );
Map encMap = this.webAppConfig.getLocaleEncodingMap( );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneResponse.LookForLocaleEncoding",
new String[] {localeString, encMap + ""} );
String fullMatch = ( String ) encMap.get( localeString );
if ( fullMatch != null ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneResponse.FoundLocaleEncoding", fullMatch );
return fullMatch;
} else {
localeString = loc.getLanguage( );
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneResponse.LookForLocaleEncoding",
new String[] {localeString, encMap + ""} );
String match = ( String ) encMap.get( localeString );
if ( match != null ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneResponse.FoundLocaleEncoding", match );
}
return match;
}
}
136 public void setErrorStatusCode( int statusCode ) {
this.errorStatusCode = new Integer( statusCode );
this.statusCode = statusCode;
}
141 public WinstoneOutputStream getWinstoneOutputStream( ) {
return this.outputStream;
}
145 public void setOutputStream( WinstoneOutputStream outData ) {
this.outputStream = outData;
}
149 public void setWebAppConfig( WebAppConfiguration webAppConfig ) {
this.webAppConfig = webAppConfig;
}
153 public String getProtocol( ) {
return this.protocol;
}
157 public void setProtocol( String protocol ) {
this.protocol = protocol;
}
161 public void extractRequestKeepAliveHeader( WinstoneRequest req ) {
this.reqKeepAliveHeader = req.getHeader( KEEP_ALIVE_HEADER );
}
165 public List getHeaders( ) {
return this.headers;
}
169 public List getCookies( ) {
return this.cookies;
}
173 public WinstoneRequest getRequest( ) {
return this.req;
}
177 public void setRequest( WinstoneRequest req ) {
this.req = req;
}
181 public void startIncludeBuffer( ) {
this.outputStream.startIncludeBuffer( );
}
185 public void finishIncludeBuffer( ) throws IOException {
if ( isIncluding( ) ) {
if ( this.outputWriter != null ) {
this.outputWriter.flush( );
}
this.outputStream.finishIncludeBuffer( );
}
}
194 public void clearIncludeStackForForward( ) throws IOException {
this.outputStream.clearIncludeStackForForward( );
}
198 protected static String getCharsetFromContentTypeHeader( String type, StringBuffer remainder ) {
if ( type == null ) {
return null;
}
// Parse type to set encoding if needed
StringTokenizer st = new StringTokenizer( type, ";" );
String localEncoding = null;
while ( st.hasMoreTokens( ) ) {
String clause = st.nextToken( ).trim( );
if ( clause.startsWith( "charset=" ) )
localEncoding = clause.substring( 8 );
else {
if ( remainder.length( ) > 0 ) {
remainder.append( ";" );
}
remainder.append( clause );
}
}
if ( ( localEncoding == null ) ||
!localEncoding.startsWith( "\"" ) ||
!localEncoding.endsWith( "\"" ) ) {
return localEncoding;
} else {
return localEncoding.substring( 1, localEncoding.length( ) - 1 );
}
}
/**
* This ensures the bare minimum correct http headers are present
*/
228 public void validateHeaders( ) {
// Need this block for WebDAV support. "Connection:close" header is ignored
String lengthHeader = getHeader( CONTENT_LENGTH_HEADER );
if ( ( lengthHeader == null ) && ( this.statusCode >= 300 ) ) {
int bodyBytes = this.outputStream.getOutputStreamLength( );
if ( getBufferSize( ) > bodyBytes ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES,
"WinstoneResponse.ForcingContentLength", "" + bodyBytes );
forceHeader( CONTENT_LENGTH_HEADER, "" + bodyBytes );
lengthHeader = getHeader( CONTENT_LENGTH_HEADER );
}
}
forceHeader( KEEP_ALIVE_HEADER, !closeAfterRequest( ) ? KEEP_ALIVE_OPEN : KEEP_ALIVE_CLOSE );
String contentType = getHeader( CONTENT_TYPE_HEADER );
if ( this.statusCode != SC_MOVED_TEMPORARILY ) {
if ( contentType == null ) {
// Bypass normal encoding
forceHeader( CONTENT_TYPE_HEADER, "text/html;charset=" + getCharacterEncoding( ) );
} else if ( contentType.startsWith( "text/" ) ) {
// replace charset in content
StringBuffer remainder = new StringBuffer( );
getCharsetFromContentTypeHeader( contentType, remainder );
forceHeader( CONTENT_TYPE_HEADER, remainder.toString( ) + ";charset=" + getCharacterEncoding( ) );
}
}
if ( getHeader( DATE_HEADER ) == null ) {
forceHeader( DATE_HEADER, formatHeaderDate( new Date( ) ) );
}
if ( getHeader( X_POWERED_BY_HEADER ) == null ) {
forceHeader( X_POWERED_BY_HEADER, X_POWERED_BY_HEADER_VALUE );
}
if ( this.locale != null ) {
String lang = this.locale.getLanguage( );
if ( ( this.locale.getCountry( ) != null ) && !this.locale.getCountry( ).equals( "" ) ) {
lang = lang + "-" + this.locale.getCountry( );
}
forceHeader( CONTENT_LANGUAGE_HEADER, lang );
}
// If we don't have a webappConfig, exit here, cause we definitely don't
// have a session
if ( req.getWebAppConfig( ) == null ) {
return;
}
// Write out the new session cookie if it's present
HostConfiguration hostConfig = req.getHostGroup( ).getHostByName( req.getServerName( ) );
for ( Iterator i = req.getCurrentSessionIds( ).keySet( ).iterator( ); i.hasNext( ); ) {
String prefix = ( String ) i.next( );
String sessionId = ( String ) req.getCurrentSessionIds( ).get( prefix );
WebAppConfiguration ownerContext = hostConfig.getWebAppByURI( prefix );
if ( ownerContext != null ) {
WinstoneSession session = ownerContext.getSessionById( sessionId, true );
if ( ( session != null ) && session.isNew( ) ) {
session.setIsNew( false );
Cookie cookie = new Cookie( WinstoneSession.SESSION_COOKIE_NAME, session.getId( ) );
cookie.setMaxAge( -1 );
cookie.setSecure( req.isSecure( ) );
cookie.setVersion( 0 ); //req.isSecure( ) ? 1 : 0 );
cookie.setPath( req.getWebAppConfig( ).getContextPath( ).equals( "" ) ? "/"
: req.getWebAppConfig( ).getContextPath( ) );
this.cookies.add( cookie ); // don't call addCookie because we might be including
}
}
}
// Look for expired sessions: ie ones where the requested and current ids are different
for ( Iterator i = req.getRequestedSessionIds( ).keySet( ).iterator( ); i.hasNext( ); ) {
String prefix = ( String ) i.next( );
String sessionId = ( String ) req.getRequestedSessionIds( ).get( prefix );
if ( !req.getCurrentSessionIds( ).containsKey( prefix ) ) {
Cookie cookie = new Cookie( WinstoneSession.SESSION_COOKIE_NAME, sessionId );
cookie.setMaxAge( 0 ); // explicitly expire this cookie
cookie.setSecure( req.isSecure( ) );
cookie.setVersion( 0 ); //req.isSecure( ) ? 1 : 0 );
cookie.setPath( prefix.equals( "" ) ? "/" : prefix );
this.cookies.add( cookie ); // don't call addCookie because we might be including
}
}
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WinstoneResponse.HeadersPreCommit",
this.headers + "" );
}
/**
* Writes out the http header for a single cookie
*/
315 public String writeCookie( Cookie cookie ) throws IOException {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WinstoneResponse.WritingCookie", cookie + "" );
StringBuffer out = new StringBuffer( );
// Set-Cookie or Set-Cookie2
if ( cookie.getVersion( ) >= 1 )
out.append( OUT_COOKIE_HEADER1 ).append( ": " ); // TCK doesn't like set-cookie2
else
out.append( OUT_COOKIE_HEADER1 ).append( ": " );
// name/value pair
if ( cookie.getVersion( ) == 0 )
out.append( cookie.getName( ) ).append( "=" ).append( cookie.getValue( ) );
else {
out.append( cookie.getName( ) ).append( "=" );
quote( cookie.getValue( ), out );
}
if ( cookie.getVersion( ) >= 1 ) {
out.append( "; Version=1" );
if ( cookie.getDomain( ) != null ) {
out.append( "; Domain=" );
quote( cookie.getDomain( ), out );
}
if ( cookie.getSecure( ) )
out.append( "; Secure" );
if ( cookie.getMaxAge( ) >= 0 )
out.append( "; Max-Age=" ).append( cookie.getMaxAge( ) );
else
out.append( "; Discard" );
if ( cookie.getPath( ) != null ) {
out.append( "; Path=" );
quote( cookie.getPath( ), out );
}
} else {
if ( cookie.getDomain( ) != null ) {
out.append( "; Domain=" );
out.append( cookie.getDomain( ) );
}
if ( cookie.getMaxAge( ) > 0 ) {
long expiryMS = System.currentTimeMillis( )
+ ( 1000 * ( long ) cookie.getMaxAge( ) );
String expiryDate = null;
synchronized ( VERSION0_DF ) {
expiryDate = VERSION0_DF.format( new Date( expiryMS ) );
}
out.append( "; Expires=" ).append( expiryDate );
} else if ( cookie.getMaxAge( ) == 0 ) {
String expiryDate = null;
synchronized ( VERSION0_DF ) {
expiryDate = VERSION0_DF.format( new Date( 5000 ) );
}
out.append( "; Expires=" ).append( expiryDate );
}
if ( cookie.getPath( ) != null )
out.append( "; Path=" ).append( cookie.getPath( ) );
if ( cookie.getSecure( ) )
out.append( "; Secure" );
}
return out.toString( );
}
379 private static String formatHeaderDate( Date dateIn ) {
String date = null;
synchronized ( HTTP_DF ) {
date = HTTP_DF.format( dateIn );
}
return date;
}
/**
* Quotes the necessary strings in a cookie header. The quoting is only
* applied if the string contains special characters.
*/
391 protected static void quote( String value, StringBuffer out ) {
if ( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
out.append( value );
} else {
boolean containsSpecial = false;
for ( int n = 0; n < value.length( ); n++ ) {
char thisChar = value.charAt( n );
if ( ( thisChar < 32 ) || ( thisChar >= 127 )
|| ( specialCharacters.indexOf( thisChar ) != -1 ) ) {
containsSpecial = true;
break;
}
}
if ( containsSpecial )
out.append( '"' ).append( value ).append( '"' );
else
out.append( value );
}
}
411 private static final String specialCharacters = "( )<>@, ;:\\\"/[]?={} \t";
/**
* Based on request/response headers and the protocol, determine whether or
* not this connection should operate in keep-alive mode.
*/
417 public boolean closeAfterRequest( ) {
String inKeepAliveHeader = this.reqKeepAliveHeader;
String outKeepAliveHeader = getHeader( KEEP_ALIVE_HEADER );
boolean hasContentLength = ( getHeader( CONTENT_LENGTH_HEADER ) != null );
if ( this.protocol.startsWith( "HTTP/0" ) )
return true;
else if ( ( inKeepAliveHeader == null ) && ( outKeepAliveHeader == null ) )
return this.protocol.equals( "HTTP/1.0" ) ? true : !hasContentLength;
else if ( outKeepAliveHeader != null )
return outKeepAliveHeader.equalsIgnoreCase( KEEP_ALIVE_CLOSE ) || !hasContentLength;
else if ( inKeepAliveHeader != null )
return inKeepAliveHeader.equalsIgnoreCase( KEEP_ALIVE_CLOSE ) || !hasContentLength;
else
return false;
}
// ServletResponse interface methods
434 public void flushBuffer( ) throws IOException {
if ( this.outputWriter != null ) {
this.outputWriter.flush( );
}
this.outputStream.flush( );
}
441 public void setBufferSize( int size ) {
this.outputStream.setBufferSize( size );
}
445 public int getBufferSize( ) {
return this.outputStream.getBufferSize( );
}
449 public String getCharacterEncoding( ) {
String enc = getCurrentEncoding( );
return ( enc == null ? "ISO-8859-1" : enc );
}
454 public void setCharacterEncoding( String encoding ) {
if ( ( this.outputWriter == null ) && !isCommitted( ) ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WinstoneResponse.SettingEncoding", encoding );
this.explicitEncoding = encoding;
correctContentTypeHeaderEncoding( encoding );
}
}
462 private void correctContentTypeHeaderEncoding( String encoding ) {
String contentType = getContentType( );
if ( contentType != null ) {
StringBuffer remainderHeader = new StringBuffer( );
getCharsetFromContentTypeHeader( contentType, remainderHeader );
if ( remainderHeader.length( ) != 0 ) {
forceHeader( CONTENT_TYPE_HEADER, remainderHeader + ";charset=" + encoding );
}
}
}
473 public String getContentType( ) {
return getHeader( CONTENT_TYPE_HEADER );
}
477 public void setContentType( String type ) {
setHeader( CONTENT_TYPE_HEADER, type );
}
481 public Locale getLocale( ) {
return this.locale == null ? Locale.getDefault( ) : this.locale;
}
485 private boolean isIncluding( ) {
return this.outputStream.isIncluding( );
}
489 public void setLocale( Locale loc ) {
if ( isIncluding( ) ) {
return;
} else if ( isCommitted( ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneResponse.SetLocaleTooLate" );
} else {
if ( ( this.outputWriter == null ) && ( this.explicitEncoding == null ) ) {
String localeEncoding = getEncodingFromLocale( loc );
if ( localeEncoding != null ) {
this.implicitEncoding = localeEncoding;
correctContentTypeHeaderEncoding( localeEncoding );
}
}
this.locale = loc;
}
}
507 public ServletOutputStream getOutputStream( ) throws IOException {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WinstoneResponse.GetOutputStream" );
return this.outputStream;
}
512 public PrintWriter getWriter( ) throws IOException {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WinstoneResponse.GetWriter" );
if ( this.outputWriter != null )
return this.outputWriter;
else {
this.outputWriter = new WinstoneResponseWriter( this.outputStream, this );
return this.outputWriter;
}
}
522 public boolean isCommitted( ) {
return this.outputStream.isCommitted( );
}
526 public void reset( ) {
if ( !isIncluding( ) ) {
resetBuffer( );
this.statusCode = SC_OK;
this.headers.clear( );
this.cookies.clear( );
}
}
535 public void resetBuffer( ) {
if ( !isIncluding( ) ) {
if ( isCommitted( ) )
throw new IllegalStateException( Launcher.RESOURCES
.getString( "WinstoneResponse.ResponseCommitted" ) );
// Disregard any output temporarily while we flush
this.outputStream.setDisregardMode( true );
if ( this.outputWriter != null ) {
this.outputWriter.flush( );
}
this.outputStream.setDisregardMode( false );
this.outputStream.reset( );
}
}
553 public void setContentLength( int len ) {
setIntHeader( CONTENT_LENGTH_HEADER, len );
}
// HttpServletResponse interface methods
558 public void addCookie( Cookie cookie ) {
if ( !isIncluding( ) ) {
this.cookies.add( cookie );
}
}
564 public boolean containsHeader( String name ) {
for ( int n = 0; n < this.headers.size( ); n++ )
if ( ( ( String ) this.headers.get( n ) ).startsWith( name ) )
return true;
return false;
}
571 public void addDateHeader( String name, long date ) {
addHeader( name, formatHeaderDate( new Date( date ) ) );
} // df.format( new Date( date ) ) );}
575 public void addIntHeader( String name, int value ) {
addHeader( name, "" + value );
}
579 public void addHeader( String name, String value ) {
if ( isIncluding( ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneResponse.HeaderInInclude",
new String[] {name, value} );
} else if ( isCommitted( ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneResponse.HeaderAfterCommitted",
new String[] {name, value} );
} else if ( value != null ) {
if ( name.equals( CONTENT_TYPE_HEADER ) ) {
StringBuffer remainderHeader = new StringBuffer( );
String headerEncoding = getCharsetFromContentTypeHeader( value, remainderHeader );
if ( this.outputWriter != null ) {
value = remainderHeader + ";charset=" + getCharacterEncoding( );
} else if ( headerEncoding != null ) {
this.explicitEncoding = headerEncoding;
}
}
this.headers.add( name + ": " + value );
}
}
600 public void setDateHeader( String name, long date ) {
setHeader( name, formatHeaderDate( new Date( date ) ) );
}
604 public void setIntHeader( String name, int value ) {
setHeader( name, "" + value );
}
608 public void setHeader( String name, String value ) {
if ( isIncluding( ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneResponse.HeaderInInclude",
new String[] {name, value} );
} else if ( isCommitted( ) ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES, "WinstoneResponse.HeaderAfterCommitted",
new String[] {name, value} );
} else {
boolean found = false;
for ( int n = 0; ( n < this.headers.size( ) ); n++ ) {
String header = ( String ) this.headers.get( n );
if ( header.startsWith( name + ": " ) ) {
if ( found ) {
this.headers.remove( n );
continue;
}
if ( name.equals( CONTENT_TYPE_HEADER ) ) {
if ( value != null ) {
StringBuffer remainderHeader = new StringBuffer( );
String headerEncoding = getCharsetFromContentTypeHeader(
value, remainderHeader );
if ( this.outputWriter != null ) {
value = remainderHeader + ";charset=" + getCharacterEncoding( );
} else if ( headerEncoding != null ) {
this.explicitEncoding = headerEncoding;
}
}
}
if ( value != null ) {
this.headers.set( n, name + ": " + value );
} else {
this.headers.remove( n );
}
found = true;
}
}
if ( !found ) {
addHeader( name, value );
}
}
}
651 private void forceHeader( String name, String value ) {
boolean found = false;
for ( int n = 0; ( n < this.headers.size( ) ); n++ ) {
String header = ( String ) this.headers.get( n );
if ( header.startsWith( name + ": " ) ) {
found = true;
this.headers.set( n, name + ": " + value );
}
}
if ( !found ) {
this.headers.add( name + ": " + value );
}
}
665 private String getCurrentEncoding( ) {
if ( this.explicitEncoding != null ) {
return this.explicitEncoding;
} else if ( this.implicitEncoding != null ) {
return this.implicitEncoding;
} else if ( ( this.req != null ) && ( this.req.getCharacterEncoding( ) != null ) ) {
try {
"0".getBytes( this.req.getCharacterEncoding( ) );
return this.req.getCharacterEncoding( );
} catch ( UnsupportedEncodingException err ) {
return null;
}
} else {
return null;
}
}
682 public String getHeader( String name ) {
for ( int n = 0; n < this.headers.size( ); n++ ) {
String header = ( String ) this.headers.get( n );
if ( header.startsWith( name + ": " ) )
return header.substring( name.length( ) + 2 );
}
return null;
}
691 public String encodeRedirectURL( String url ) {
return url;
}
695 public String encodeURL( String url ) {
return url;
}
699 public int getStatus( ) {
return this.statusCode;
}
703 public Integer getErrorStatusCode( ) {
return this.errorStatusCode;
}
707 public void setStatus( int sc ) {
if ( !isIncluding( ) && ( this.errorStatusCode == null ) ) {
// if ( !isIncluding( ) ) {
this.statusCode = sc;
// if ( this.errorStatusCode != null ) {
// this.errorStatusCode = new Integer( sc );
// }
}
}
717 public void sendRedirect( String location ) throws IOException {
if ( isIncluding( ) ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "IncludeResponse.Redirect",
location );
return;
} else if ( isCommitted( ) ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneOutputStream.AlreadyCommitted" ) );
}
resetBuffer( );
// Build location
StringBuffer fullLocation = new StringBuffer( );
if ( location.startsWith( "http://" ) || location.startsWith( "https://" ) ) {
fullLocation.append( location );
} else {
if ( location.trim( ).equals( "." ) ) {
location = "";
}
fullLocation.append( this.req.getScheme( ) ).append( "://" );
fullLocation.append( this.req.getServerName( ) );
if ( !( ( this.req.getServerPort( ) == 80 ) && this.req.getScheme( ).equals( "http" ) )
&& !( ( this.req.getServerPort( ) == 443 ) && this.req.getScheme( ).equals( "https" ) ) )
fullLocation.append( ':' ).append( this.req.getServerPort( ) );
if ( location.startsWith( "/" ) ) {
fullLocation.append( location );
} else {
fullLocation.append( this.req.getRequestURI( ) );
int questionPos = fullLocation.toString( ).indexOf( "?" );
if ( questionPos != -1 ) {
fullLocation.delete( questionPos, fullLocation.length( ) );
}
fullLocation.delete(
fullLocation.toString( ).lastIndexOf( "/" ) + 1,
fullLocation.length( ) );
fullLocation.append( location );
}
}
if ( this.req != null ) {
this.req.discardRequestBody( );
}
this.statusCode = HttpServletResponse.SC_MOVED_TEMPORARILY;
setHeader( LOCATION_HEADER, fullLocation.toString( ) );
setContentLength( 0 );
getWriter( ).flush( );
}
764 public void sendError( int sc ) throws IOException {
sendError( sc, null );
}
768 public void sendError( int sc, String msg ) throws IOException {
if ( isIncluding( ) ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "IncludeResponse.Error",
new String[] { "" + sc, msg } );
return;
}
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WinstoneResponse.SendingError", new String[] { "" + sc, msg } );
if ( ( this.webAppConfig != null ) && ( this.req != null ) ) {
RequestDispatcher rd = this.webAppConfig
.getErrorDispatcherByCode( sc, msg, null );
if ( rd != null ) {
try {
rd.forward( this.req, this );
return;
} catch ( IllegalStateException err ) {
throw err;
} catch ( IOException err ) {
throw err;
} catch ( Throwable err ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneResponse.ErrorInErrorPage", new String[] {
rd.getName( ), sc + "" }, err );
return;
}
}
}
// If we are here there was no webapp and/or no request object, so
// show the default error page
if ( this.errorStatusCode == null ) {
this.statusCode = sc;
}
String output = Launcher.RESOURCES.getString( "WinstoneResponse.ErrorPage",
new String[] { sc + "", ( msg == null ? "" : msg ), "",
Launcher.RESOURCES.getString( "ServerVersion" ),
"" + new Date( ) } );
setContentLength( output.getBytes( getCharacterEncoding( ) ).length );
Writer out = getWriter( );
out.write( output );
out.flush( );
}
/**
* @deprecated
*/
816 public String encodeRedirectUrl( String url ) {
return encodeRedirectURL( url );
}
/**
* @deprecated
*/
823 public String encodeUrl( String url ) {
return encodeURL( url );
}
/**
* @deprecated
*/
830 public void setStatus( int sc, String sm ) {
setStatus( sc );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
/**
* A hacked print writer that allows us to trigger an automatic flush on
* println operations that go over the content length or buffer size.
*
* This is only necessary because the spec authors seem intent of having
* the print writer's flushing behaviour be half auto-flush and half not.
* Damned if I know why - seems unnecessary and confusing to me.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneResponseWriter.java, v 1.3 2006/02/28 07:32:47 rickknowles Exp $
*/
25 public class WinstoneResponseWriter extends PrintWriter {
27 private WinstoneOutputStream outputStream;
28 private WinstoneResponse response;
private int bytesBuffered;
31 public WinstoneResponseWriter( WinstoneOutputStream out,
32 WinstoneResponse response ) throws UnsupportedEncodingException {
super( new OutputStreamWriter( out, response.getCharacterEncoding( ) ), false );
this.outputStream = out;
this.response = response;
this.bytesBuffered = 0;
}
39 public void write( int c ) {
super.write( c );
appendByteCount( "" + ( ( char ) c ) );
}
44 public void write( char[] buf, int off, int len ) {
super.write( buf, off, len );
if ( buf != null ) {
appendByteCount( new String( buf, off, len ) );
}
}
51 public void write( String s, int off, int len ) {
super.write( s, off, len );
if ( s != null ) {
appendByteCount( s.substring( off, len ) );
}
}
58 protected void appendByteCount( String input ) {
try {
this.bytesBuffered += input.getBytes( response.getCharacterEncoding( ) ).length;
} catch ( IOException err ) {/* impossible */}
}
65 public void println( ) {
super.println( );
simulateAutoFlush( );
}
70 public void flush( ) {
super.flush( );
this.bytesBuffered = 0;
}
75 protected void simulateAutoFlush( ) {
String contentLengthHeader = response.getHeader( WinstoneResponse.CONTENT_LENGTH_HEADER );
if ( ( contentLengthHeader != null ) &&
( ( this.outputStream.getOutputStreamLength( ) + this.bytesBuffered ) >=
Integer.parseInt( contentLengthHeader ) ) ) {
Logger.log( Logger.FULL_DEBUG, Launcher.RESOURCES, "WinstoneResponseWriter.AutoFlush",
new String[] {contentLengthHeader,
( this.outputStream.getOutputStreamLength( ) + this.bytesBuffered ) + ""} );
flush( );
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* Http session implementation for Winstone.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneSession.java, v 1.10 2006/08/27 07:19:47 rickknowles Exp $
*/
44 public class WinstoneSession implements HttpSession, Serializable {
45 public static final String SESSION_COOKIE_NAME = "JSESSIONID";
47 private String sessionId;
48 private WebAppConfiguration webAppConfig;
49 private Map sessionData;
private long createTime;
private long lastAccessedTime;
private int maxInactivePeriod;
private boolean isNew;
private boolean isInvalidated;
55 private HttpSessionAttributeListener sessionAttributeListeners[];
56 private HttpSessionListener sessionListeners[];
57 private HttpSessionActivationListener sessionActivationListeners[];
private boolean distributable;
59 private Object sessionMonitor = new Boolean( true );
60 private Set requestsUsingMe;
/**
* Constructor
*/
65 public WinstoneSession( String sessionId ) {
this.sessionId = sessionId;
this.sessionData = new HashMap( );
this.requestsUsingMe = new HashSet( );
this.createTime = System.currentTimeMillis( );
this.isNew = true;
this.isInvalidated = false;
}
74 public void setWebAppConfiguration( WebAppConfiguration webAppConfig ) {
this.webAppConfig = webAppConfig;
this.distributable = webAppConfig.isDistributable( );
}
79 public void sendCreatedNotifies( ) {
// Notify session listeners of new session
for ( int n = 0; n < this.sessionListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
this.sessionListeners[n].sessionCreated( new HttpSessionEvent( this ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
89 public void setSessionActivationListeners(
90 HttpSessionActivationListener listeners[] ) {
this.sessionActivationListeners = listeners;
}
94 public void setSessionAttributeListeners(
95 HttpSessionAttributeListener listeners[] ) {
this.sessionAttributeListeners = listeners;
}
99 public void setSessionListeners( HttpSessionListener listeners[] ) {
this.sessionListeners = listeners;
}
103 public void setLastAccessedDate( long time ) {
this.lastAccessedTime = time;
}
107 public void setIsNew( boolean isNew ) {
this.isNew = isNew;
}
111 public void addUsed( WinstoneRequest request ) {
this.requestsUsingMe.add( request );
}
115 public void removeUsed( WinstoneRequest request ) {
this.requestsUsingMe.remove( request );
}
119 public boolean isUnusedByRequests( ) {
return this.requestsUsingMe.isEmpty( );
}
123 public boolean isExpired( ) {
// check if it's expired yet
long nowDate = System.currentTimeMillis( );
long maxInactive = getMaxInactiveInterval( ) * 1000;
return ( ( maxInactive > 0 ) && ( nowDate - this.lastAccessedTime > maxInactive ) );
}
// Implementation methods
131 public Object getAttribute( String name ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
Object att = null;
synchronized ( this.sessionMonitor ) {
att = this.sessionData.get( name );
}
return att;
}
142 public Enumeration getAttributeNames( ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
Enumeration names = null;
synchronized ( this.sessionMonitor ) {
names = Collections.enumeration( this.sessionData.keySet( ) );
}
return names;
}
153 public void setAttribute( String name, Object value ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
// Check for serializability if distributable
if ( this.distributable && ( value != null )
&& !( value instanceof java.io.Serializable ) )
throw new IllegalArgumentException( Launcher.RESOURCES.getString(
"WinstoneSession.AttributeNotSerializable", new String[] {
name, value.getClass( ).getName( ) } ) );
// valueBound must be before binding
if ( value instanceof HttpSessionBindingListener ) {
HttpSessionBindingListener hsbl = ( HttpSessionBindingListener ) value;
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
hsbl.valueBound( new HttpSessionBindingEvent( this, name, value ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
Object oldValue = null;
synchronized ( this.sessionMonitor ) {
oldValue = this.sessionData.get( name );
if ( value == null ) {
this.sessionData.remove( name );
} else {
this.sessionData.put( name, value );
}
}
// valueUnbound must be after unbinding
if ( oldValue instanceof HttpSessionBindingListener ) {
HttpSessionBindingListener hsbl = ( HttpSessionBindingListener ) oldValue;
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
hsbl.valueUnbound( new HttpSessionBindingEvent( this, name, oldValue ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
// Notify other listeners
if ( oldValue != null )
for ( int n = 0; n < this.sessionAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
this.sessionAttributeListeners[n].attributeReplaced(
new HttpSessionBindingEvent( this, name, oldValue ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
else
for ( int n = 0; n < this.sessionAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
this.sessionAttributeListeners[n].attributeAdded(
new HttpSessionBindingEvent( this, name, value ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
213 public void removeAttribute( String name ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
Object value = null;
synchronized ( this.sessionMonitor ) {
value = this.sessionData.get( name );
this.sessionData.remove( name );
}
// Notify listeners
if ( value instanceof HttpSessionBindingListener ) {
HttpSessionBindingListener hsbl = ( HttpSessionBindingListener ) value;
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
hsbl.valueUnbound( new HttpSessionBindingEvent( this, name ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
if ( value != null )
for ( int n = 0; n < this.sessionAttributeListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
this.sessionAttributeListeners[n].attributeRemoved(
new HttpSessionBindingEvent( this, name, value ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
241 public long getCreationTime( ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
return this.createTime;
}
248 public long getLastAccessedTime( ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
return this.lastAccessedTime;
}
255 public String getId( ) {
return this.sessionId;
}
259 public int getMaxInactiveInterval( ) {
return this.maxInactivePeriod;
}
263 public void setMaxInactiveInterval( int interval ) {
this.maxInactivePeriod = interval;
}
267 public boolean isNew( ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
return this.isNew;
}
274 public ServletContext getServletContext( ) {
return this.webAppConfig;
}
278 public void invalidate( ) {
if ( this.isInvalidated ) {
throw new IllegalStateException( Launcher.RESOURCES.getString( "WinstoneSession.InvalidatedSession" ) );
}
// Notify session listeners of invalidated session -- backwards
for ( int n = this.sessionListeners.length - 1; n >= 0; n-- ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
this.sessionListeners[n].sessionDestroyed( new HttpSessionEvent( this ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
List keys = new ArrayList( this.sessionData.keySet( ) );
for ( Iterator i = keys.iterator( ); i.hasNext( ); )
removeAttribute( ( String ) i.next( ) );
synchronized ( this.sessionMonitor ) {
this.sessionData.clear( );
}
this.isInvalidated = true;
this.webAppConfig.removeSessionById( this.sessionId );
}
/**
* Called after the session has been serialized to another server.
*/
303 public void passivate( ) {
// Notify session listeners of invalidated session
for ( int n = 0; n < this.sessionActivationListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
this.sessionActivationListeners[n].sessionWillPassivate(
new HttpSessionEvent( this ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
// Question: Is passivation equivalent to invalidation ? Should all
// entries be removed ?
// List keys = new ArrayList( this.sessionData.keySet( ) );
// for ( Iterator i = keys.iterator( ); i.hasNext( ); )
// removeAttribute( ( String ) i.next( ) );
synchronized ( this.sessionMonitor ) {
this.sessionData.clear( );
}
this.webAppConfig.removeSessionById( this.sessionId );
}
/**
* Called after the session has been deserialized from another server.
*/
327 public void activate( WebAppConfiguration webAppConfig ) {
this.webAppConfig = webAppConfig;
webAppConfig.setSessionListeners( this );
// Notify session listeners of invalidated session
for ( int n = 0; n < this.sessionActivationListeners.length; n++ ) {
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( this.webAppConfig.getLoader( ) );
this.sessionActivationListeners[n].sessionDidActivate(
new HttpSessionEvent( this ) );
Thread.currentThread( ).setContextClassLoader( cl );
}
}
/**
* Save this session to the temp dir defined for this webapp
*/
344 public void saveToTemp( ) {
File toDir = getSessionTempDir( this.webAppConfig );
synchronized ( this.sessionMonitor ) {
OutputStream out = null;
ObjectOutputStream objOut = null;
try {
File toFile = new File( toDir, this.sessionId + ".ser" );
out = new FileOutputStream( toFile, false );
objOut = new ObjectOutputStream( out );
objOut.writeObject( this );
} catch ( IOException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WinstoneSession.ErrorSavingSession", err );
} finally {
if ( objOut != null ) {
try {objOut.close( );} catch ( IOException err ) {}
}
if ( out != null ) {
try {out.close( );} catch ( IOException err ) {}
}
}
}
}
368 public static File getSessionTempDir( WebAppConfiguration webAppConfig ) {
File tmpDir = ( File ) webAppConfig.getAttribute( "javax.servlet.context.tempdir" );
File sessionsDir = new File( tmpDir, "WEB-INF" + File.separator + "winstoneSessions" );
if ( !sessionsDir.exists( ) ) {
sessionsDir.mkdirs( );
}
return sessionsDir;
}
377 public static void loadSessions( WebAppConfiguration webAppConfig ) {
int expiredCount = 0;
// Iterate through the files in the dir, instantiate and then add to the sessions set
File tempDir = getSessionTempDir( webAppConfig );
File possibleSessionFiles[] = tempDir.listFiles( );
for ( int n = 0; n < possibleSessionFiles.length; n++ ) {
if ( possibleSessionFiles[n].getName( ).endsWith( ".ser" ) ) {
InputStream in = null;
ObjectInputStream objIn = null;
try {
in = new FileInputStream( possibleSessionFiles[n] );
objIn = new ObjectInputStream( in );
WinstoneSession session = ( WinstoneSession ) objIn.readObject( );
session.setWebAppConfiguration( webAppConfig );
webAppConfig.setSessionListeners( session );
if ( session.isExpired( ) ) {
session.invalidate( );
expiredCount++;
} else {
webAppConfig.addSession( session.getId( ), session );
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WinstoneSession.RestoredSession", session.getId( ) );
}
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES,
"WinstoneSession.ErrorLoadingSession", err );
} finally {
if ( objIn != null ) {
try {objIn.close( );} catch ( IOException err ) {}
}
if ( in != null ) {
try {in.close( );} catch ( IOException err ) {}
}
possibleSessionFiles[n].delete( );
}
}
}
if ( expiredCount > 0 ) {
Logger.log( Logger.DEBUG, Launcher.RESOURCES,
"WebAppConfig.InvalidatedSessions", expiredCount + "" );
}
}
/**
* Serialization implementation. This makes sure to only serialize the parts
* we want to send to another server.
*
* @param out
* The stream to write the contents to
* @throws IOException
*/
428 private void writeObject( java.io.ObjectOutputStream out ) throws IOException {
out.writeUTF( sessionId );
out.writeLong( createTime );
out.writeLong( lastAccessedTime );
out.writeInt( maxInactivePeriod );
out.writeBoolean( isNew );
out.writeBoolean( distributable );
// Write the map, but first remove non-serializables
Map copy = new HashMap( sessionData );
Set keys = new HashSet( copy.keySet( ) );
for ( Iterator i = keys.iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
if ( !( copy.get( key ) instanceof Serializable ) ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES,
"WinstoneSession.SkippingNonSerializable",
new String[] { key,
copy.get( key ).getClass( ).getName( ) } );
}
copy.remove( key );
}
out.writeInt( copy.size( ) );
for ( Iterator i = copy.keySet( ).iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
out.writeUTF( key );
out.writeObject( copy.get( key ) );
}
}
/**
* Deserialization implementation
*
* @param in
* The source of stream data
* @throws IOException
* @throws ClassNotFoundException
*/
465 private void readObject( java.io.ObjectInputStream in ) throws IOException,
ClassNotFoundException {
this.sessionId = in.readUTF( );
this.createTime = in.readLong( );
this.lastAccessedTime = in.readLong( );
this.maxInactivePeriod = in.readInt( );
this.isNew = in.readBoolean( );
this.distributable = in.readBoolean( );
// Read the map
this.sessionData = new Hashtable( );
this.requestsUsingMe = new HashSet( );
int entryCount = in.readInt( );
for ( int n = 0; n < entryCount; n++ ) {
String key = in.readUTF( );
Object variable = in.readObject( );
this.sessionData.put( key, variable );
}
this.sessionMonitor = new Boolean( true );
}
/**
* @deprecated
*/
489 public Object getValue( String name ) {
return getAttribute( name );
}
/**
* @deprecated
*/
496 public void putValue( String name, Object value ) {
setAttribute( name, value );
}
/**
* @deprecated
*/
503 public void removeValue( String name ) {
removeAttribute( name );
}
/**
* @deprecated
*/
510 public String[] getValueNames( ) {
return ( String[] ) this.sessionData.keySet( ).toArray( new String[0] );
}
/**
* @deprecated
*/
517 public javax.servlet.http.HttpSessionContext getSessionContext( ) {
return null;
} // deprecated
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.accesslog;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import winstone.AccessLogger;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneRequest;
import winstone.WinstoneResourceBundle;
import winstone.WinstoneResponse;
/**
* Simulates an apache "combined" style logger, which logs User-Agent, Referer, etc
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: SimpleAccessLogger.java, v 1.5 2006/03/24 17:24:19 rickknowles Exp $
*/
32 public class SimpleAccessLogger implements AccessLogger {
34 public static final WinstoneResourceBundle ACCESSLOG_RESOURCES =
new WinstoneResourceBundle( "winstone.accesslog.LocalStrings" );
37 private static final DateFormat DF = new SimpleDateFormat( "dd/MMM/yyyy:HH:mm:ss Z" );
38 private static final String COMMON = "###ip### - ###user### ###time### \"###uriLine###\" ###status### ###size###";
39 private static final String COMBINED = COMMON + " \"###referer###\" \"###userAgent###\"";
40 private static final String RESIN = COMMON + " \"###userAgent###\"";
// private WebAppConfiguration webAppConfig;
43 private OutputStream outStream;
44 private PrintWriter outWriter;
45 private String pattern;
46 private String fileName;
48 public SimpleAccessLogger( WebAppConfiguration webAppConfig, Map startupArgs )
throws IOException {
// this.webAppConfig = webAppConfig;
// Get pattern
String patternType = WebAppConfiguration.stringArg( startupArgs, "simpleAccessLogger.format", "combined" );
if ( patternType.equalsIgnoreCase( "combined" ) ) {
this.pattern = COMBINED;
} else if ( patternType.equalsIgnoreCase( "common" ) ) {
this.pattern = COMMON;
} else if ( patternType.equalsIgnoreCase( "resin" ) ) {
this.pattern = RESIN;
} else {
this.pattern = patternType;
}
// Get filename
String filePattern = WebAppConfiguration.stringArg( startupArgs, "simpleAccessLogger.file",
"logs/###host###/###webapp###_access.log" );
this.fileName = WinstoneResourceBundle.globalReplace( filePattern,
new String [][] {{"###host###", webAppConfig.getOwnerHostname( )},
{"###webapp###", webAppConfig.getContextName( )}} );
File file = new File( this.fileName );
file.getParentFile( ).mkdirs( );
this.outStream = new FileOutputStream( file, true );
this.outWriter = new PrintWriter( this.outStream, true );
Logger.log( Logger.DEBUG, ACCESSLOG_RESOURCES, "SimpleAccessLogger.Init",
new String[] {this.fileName, patternType} );
}
80 public void log( String originalURL, WinstoneRequest request, WinstoneResponse response ) {
String uriLine = request.getMethod( ) + " " + originalURL + " " + request.getProtocol( );
int status = response.getErrorStatusCode( ) == null ? response.getStatus( )
: response.getErrorStatusCode( ).intValue( );
int size = response.getWinstoneOutputStream( ).getBytesCommitted( );
String date = null;
synchronized ( DF ) {
date = DF.format( new Date( ) );
}
String logLine = WinstoneResourceBundle.globalReplace( this.pattern, new String[][] {
{"###ip###", request.getRemoteHost( )},
{"###user###", nvl( request.getRemoteUser( ) )},
{"###time###", "[" + date + "]"},
{"###uriLine###", uriLine},
{"###status###", "" + status},
{"###size###", "" + size},
{"###referer###", nvl( request.getHeader( "Referer" ) )},
{"###userAgent###", nvl( request.getHeader( "User-Agent" ) )}
} );
this.outWriter.println( logLine );
}
102 private static String nvl( String input ) {
return input == null ? "-" : input;
}
106 public void destroy( ) {
Logger.log( Logger.DEBUG, ACCESSLOG_RESOURCES, "SimpleAccessLogger.Close", this.fileName );
if ( this.outWriter != null ) {
this.outWriter.flush( );
this.outWriter.close( );
this.outWriter = null;
}
if ( this.outStream != null ) {
try {
this.outStream.close( );
} catch ( IOException err ) {}
this.outStream = null;
}
this.fileName = null;
// this.webAppConfig = null;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.ajp13;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.Map;
import winstone.Logger;
import winstone.RequestHandlerThread;
import winstone.WinstoneException;
/**
* Models a single incoming ajp13 packet.
*
* Fixes by Cory Osborn 2007/4/3 - IIS related. Thanks
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Ajp13IncomingPacket.java, v 1.6 2007/04/03 01:23:19 rickknowles Exp $
*/
27 public class Ajp13IncomingPacket {
// Server originated packet types
byte SERVER_FORWARD_REQUEST = 0x02;
// public static byte SERVER_SHUTDOWN = 0x07; //not implemented
// public static byte SERVER_PING = 0x08; //not implemented
// public static byte SERVER_CPING = 0x10; //not implemented
private int packetLength;
private byte packetBytes[];
private byte packetType;
38 private String method;
39 private String protocol;
40 private String uri;
41 private String remoteAddr;
42 private String remoteHost;
43 private String serverName;
private int serverPort;
private boolean isSSL;
46 private String headers[];
47 private Map attributes;
/**
* Constructor
*/
52 public Ajp13IncomingPacket( InputStream in,
53 RequestHandlerThread handler ) throws IOException {
// Get the incoming packet flag
byte headerBuffer[] = new byte[4];
int headerBytesRead = in.read( headerBuffer );
handler.setRequestStartTime( );
if ( headerBytesRead != 4 )
throw new WinstoneException( Ajp13Listener.AJP_RESOURCES
.getString( "Ajp13IncomingPacket.InvalidHeader" ) );
else if ( ( headerBuffer[0] != 0x12 ) || ( headerBuffer[1] != 0x34 ) )
throw new WinstoneException( Ajp13Listener.AJP_RESOURCES
.getString( "Ajp13IncomingPacket.InvalidHeader" ) );
// Read in the whole packet
packetLength = ( ( headerBuffer[2] & 0xFF ) << 8 )
+ ( headerBuffer[3] & 0xFF );
packetBytes = new byte[packetLength];
int packetBytesRead = in.read( packetBytes );
if ( packetBytesRead < packetLength )
throw new WinstoneException( Ajp13Listener.AJP_RESOURCES
.getString( "Ajp13IncomingPacket.ShortPacket" ) );
// Ajp13Listener.packetDump( packetBytes, packetBytesRead );
}
77 public byte parsePacket( String encoding ) throws IOException {
int position = 0;
this.packetType = packetBytes[position++];
if ( this.packetType != SERVER_FORWARD_REQUEST )
throw new WinstoneException( Ajp13Listener.AJP_RESOURCES.getString(
"Ajp13IncomingPacket.UnknownPacketType", this.packetType
+ "" ) );
// Check for terminator
if ( packetBytes[packetLength - 1] != ( byte ) 255 )
throw new WinstoneException( Ajp13Listener.AJP_RESOURCES
.getString( "Ajp13IncomingPacket.InvalidTerminator" ) );
this.method = decodeMethodType( packetBytes[position++] );
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.Method", method );
// Protocol
int protocolLength = readInteger( position, packetBytes, true );
position += 2;
this.protocol = ( protocolLength > -1 )
? readString( position, packetBytes, encoding, protocolLength )
: null;
position += protocolLength + 1;
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.Protocol", protocol );
// URI
int uriLength = readInteger( position, packetBytes, true );
position += 2;
this.uri = ( uriLength > -1 )
? readString( position, packetBytes, encoding, uriLength )
: null;
position += uriLength + 1;
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.URI", uri );
// Remote addr
int remoteAddrLength = readInteger( position, packetBytes, true );
position += 2;
this.remoteAddr = ( remoteAddrLength > -1 )
? readString( position, packetBytes, encoding, remoteAddrLength )
: null;
position += remoteAddrLength + 1;
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.RemoteAddress", remoteAddr );
// Remote host
int remoteHostLength = readInteger( position, packetBytes, true );
position += 2;
this.remoteHost = ( remoteHostLength > -1 )
? readString( position, packetBytes, encoding, remoteHostLength )
: null;
position += remoteHostLength + 1;
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.RemoteHost", remoteHost );
// Server name
int serverNameLength = readInteger( position, packetBytes, true );
position += 2;
this.serverName = ( serverNameLength > -1 )
? readString( position, packetBytes, encoding, serverNameLength )
: null;
position += serverNameLength + 1;
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.ServerName", serverName );
this.serverPort = readInteger( position, packetBytes, false );
position += 2;
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.ServerPort", "" + serverPort );
this.isSSL = readBoolean( position++, packetBytes );
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.SSL", "" + isSSL );
// Read headers
int headerCount = readInteger( position, packetBytes, false );
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.HeaderCount", "" + headerCount );
position += 2;
this.headers = new String[headerCount];
for ( int n = 0; n < headerCount; n++ ) {
// Header name
int headerTypeOrLength = readInteger( position, packetBytes, false );
position += 2;
String headerName = null;
if ( packetBytes[position - 2] == ( byte ) 0xA0 )
headerName = decodeHeaderType( headerTypeOrLength );
else {
headerName = readString( position, packetBytes, encoding,
headerTypeOrLength );
position += headerTypeOrLength + 1;
}
// Header value
int headerValueLength = readInteger( position, packetBytes, true );
position += 2;
this.headers[n] = headerName
+ ": "
+ ( ( headerValueLength > -1 )
? readString( position, packetBytes, encoding, headerValueLength )
: "" );
position += headerValueLength + 1;
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.Header", this.headers[n] );
}
// Attribute parsing
this.attributes = new Hashtable( );
while ( position < packetLength - 2 ) {
String attName = decodeAttributeType( packetBytes[position++] );
int attValueLength = readInteger( position, packetBytes, true );
position += 2;
String attValue = ( attValueLength > -1 )
? readString( position, packetBytes, encoding, attValueLength )
: null;
position += attValueLength + 1;
this.attributes.put( attName, attValue );
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.Attribute", new String[] { attName,
attValue } );
}
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13IncomingPacket.SuccessfullyReadRequest", ""
+ packetLength );
return this.packetType;
}
208 public int getPacketLength( ) {
return this.packetLength;
}
212 public String getMethod( ) {
return this.method;
}
216 public String getProtocol( ) {
return this.protocol;
}
220 public String getURI( ) {
return this.uri;
}
224 public String getRemoteAddress( ) {
return this.remoteAddr;
}
228 public String getRemoteHost( ) {
return this.remoteHost;
}
232 public String getServerName( ) {
return this.serverName;
}
236 public int getServerPort( ) {
return this.serverPort;
}
240 public boolean isSSL( ) {
return this.isSSL;
}
244 public String[] getHeaders( ) {
return this.headers;
}
248 public Map getAttributes( ) {
return this.attributes;
}
/**
* Read a single integer from the stream
*/
255 private int readInteger( int position, byte packet[], boolean forStringLength ) {
if ( forStringLength && ( packet[position] == ( byte ) 0xFF )
&& ( packet[position + 1] == ( byte ) 0xFF ) )
return -1;
else
return ( ( packet[position] & 0xFF ) << 8 )
+ ( packet[position + 1] & 0xFF );
}
/**
* Read a single boolean from the stream
*/
267 private boolean readBoolean( int position, byte packet[] ) {
return ( packet[position] == ( byte ) 1 );
}
/**
* Read a single string from the stream
*/
274 private String readString( int position, byte packet[], String encoding,
int length ) throws UnsupportedEncodingException {
// System.out.println( "Reading string length: " + length +
// " position=" + position + " packetLength=" + packet.length );
return length == 0 ? ""
: new String( packet, position, length, encoding );
}
/**
* Decodes the method types into Winstone HTTP method strings
*/
285 private String decodeMethodType( byte methodType ) {
switch ( methodType ) {
case 1:
return "OPTIONS";
case 2:
return "GET";
case 3:
return "HEAD";
case 4:
return "POST";
case 5:
return "PUT";
case 6:
return "DELETE";
case 7:
return "TRACE";
case 8:
return "PROPFIND";
case 9:
return "PROPPATCH";
case 10:
return "MKCOL";
case 11:
return "COPY";
case 12:
return "MOVE";
case 13:
return "LOCK";
case 14:
return "UNLOCK";
case 15:
return "ACL";
case 16:
return "REPORT";
case 17:
return "VERSION-CONTROL";
case 18:
return "CHECKIN";
case 19:
return "CHECKOUT";
case 20:
return "UNCHECKOUT";
case 21:
return "SEARCH";
case 22:
return "MKWORKSPACE";
case 23:
return "UPDATE";
case 24:
return "LABEL";
case 25:
return "MERGE";
case 26:
return "BASELINE_CONTROL";
case 27:
return "MKACTIVITY";
default:
return "UNKNOWN";
}
}
/**
* Decodes the header types into Winstone HTTP header strings
*/
349 private String decodeHeaderType( int headerType ) {
switch ( headerType ) {
case 0xA001:
return "Accept";
case 0xA002:
return "Accept-Charset";
case 0xA003:
return "Accept-Encoding";
case 0xA004:
return "Accept-Language";
case 0xA005:
return "Authorization";
case 0xA006:
return "Connection";
case 0xA007:
return "Content-Type";
case 0xA008:
return "Content-Length";
case 0xA009:
return "Cookie";
case 0xA00A:
return "Cookie2";
case 0xA00B:
return "Host";
case 0xA00C:
return "Pragma";
case 0xA00D:
return "Referer";
case 0xA00E:
return "User-Agent";
default:
return null;
}
}
/**
* Decodes the header types into Winstone HTTP header strings
*/
387 private String decodeAttributeType( byte attributeType ) {
switch ( attributeType ) {
case 0x01:
return "context";
case 0x02:
return "servlet_path";
case 0x03:
return "remote_user";
case 0x04:
return "auth_type";
case 0x05:
return "query_string";
case 0x06:
return "jvm_route";
case 0x07:
return "ssl_cert";
case 0x08:
return "ssl_cipher";
case 0x09:
return "ssl_session";
case 0x0A:
return "req_attribute";
default:
return null;
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.ajp13;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import winstone.HostGroup;
import winstone.Launcher;
import winstone.Listener;
import winstone.Logger;
import winstone.ObjectPool;
import winstone.RequestHandlerThread;
import winstone.WebAppConfiguration;
import winstone.WinstoneException;
import winstone.WinstoneInputStream;
import winstone.WinstoneOutputStream;
import winstone.WinstoneRequest;
import winstone.WinstoneResourceBundle;
import winstone.WinstoneResponse;
/**
* Implements the main listener daemon thread. This is the class that gets
* launched by the command line, and owns the server socket, etc.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Ajp13Listener.java, v 1.12 2006/03/24 17:24:22 rickknowles Exp $
*/
47 public class Ajp13Listener implements Listener, Runnable {
48 public final static WinstoneResourceBundle AJP_RESOURCES = new WinstoneResourceBundle( "winstone.ajp13.LocalStrings" );
private final static int LISTENER_TIMEOUT = 5000; // every 5s reset the listener socket
private final static int DEFAULT_PORT = 8009;
private final static int CONNECTION_TIMEOUT = 60000;
private final static int BACKLOG_COUNT = 1000;
private final static int KEEP_ALIVE_TIMEOUT = -1;
// private final static int KEEP_ALIVE_SLEEP = 50;
// private final static int KEEP_ALIVE_SLEEP_MAX = 500;
57 private final static String TEMPORARY_URL_STASH = "winstone.ajp13.TemporaryURLAttribute";
59 private HostGroup hostGroup;
60 private ObjectPool objectPool;
private int listenPort;
private boolean interrupted;
63 private String listenAddress;
/**
* Constructor
*/
68 public Ajp13Listener( Map args, ObjectPool objectPool, HostGroup hostGroup ) {
// Load resources
this.hostGroup = hostGroup;
this.objectPool = objectPool;
this.listenPort = Integer.parseInt( WebAppConfiguration.stringArg( args,
"ajp13Port", "" + DEFAULT_PORT ) );
this.listenAddress = WebAppConfiguration.stringArg( args,
"ajp13ListenAddress", null );
}
79 public boolean start( ) {
if ( this.listenPort < 0 ) {
return false;
} else {
this.interrupted = false;
Thread thread = new Thread( this, Launcher.RESOURCES.getString(
"Listener.ThreadName", new String[] { "ajp13",
"" + this.listenPort } ) );
thread.setDaemon( true );
thread.start( );
return true;
}
}
/**
* The main run method. This handles the normal thread processing.
*/
96 public void run( ) {
try {
ServerSocket ss = this.listenAddress == null ? new ServerSocket(
this.listenPort, BACKLOG_COUNT ) : new ServerSocket(
this.listenPort, BACKLOG_COUNT, InetAddress
.getByName( this.listenAddress ) );
ss.setSoTimeout( LISTENER_TIMEOUT );
Logger.log( Logger.INFO, AJP_RESOURCES, "Ajp13Listener.StartupOK",
this.listenPort + "" );
// Enter the main loop
while ( !interrupted ) {
// Get the listener
Socket s = null;
try {
s = ss.accept( );
} catch ( java.io.InterruptedIOException err ) {
s = null;
}
// if we actually got a socket, process it. Otherwise go around
// again
if ( s != null )
this.objectPool.handleRequest( s, this );
}
// Close server socket
ss.close( );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, AJP_RESOURCES,
"Ajp13Listener.ShutdownError", err );
}
Logger.log( Logger.INFO, AJP_RESOURCES, "Ajp13Listener.ShutdownOK" );
}
/**
* Interrupts the listener thread. This will trigger a listener shutdown
* once the so timeout has passed.
*/
136 public void destroy( ) {
this.interrupted = true;
}
/**
* Called by the request handler thread, because it needs specific setup
* code for this connection's protocol ( ie construction of request/response
* objects, in/out streams, etc ).
*
* This implementation parses incoming AJP13 packets, and builds an
* outputstream that is capable of writing back the response in AJP13
* packets.
*/
149 public void allocateRequestResponse( Socket socket, InputStream inSocket,
150 OutputStream outSocket, RequestHandlerThread handler,
boolean iAmFirst ) throws SocketException, IOException {
WinstoneRequest req = this.objectPool.getRequestFromPool( );
WinstoneResponse rsp = this.objectPool.getResponseFromPool( );
rsp.setRequest( req );
req.setHostGroup( this.hostGroup );
// rsp.updateContentTypeHeader( "text/html" );
if ( iAmFirst || ( KEEP_ALIVE_TIMEOUT == -1 ) )
socket.setSoTimeout( CONNECTION_TIMEOUT );
else
socket.setSoTimeout( KEEP_ALIVE_TIMEOUT );
Ajp13IncomingPacket headers = null;
try {
headers = new Ajp13IncomingPacket( inSocket, handler );
} catch ( InterruptedIOException err ) {
// keep alive timeout ? ignore if not first
if ( iAmFirst ) {
throw err;
} else {
deallocateRequestResponse( handler, req, rsp, null, null );
return;
}
} finally {
try {socket.setSoTimeout( CONNECTION_TIMEOUT );} catch ( Throwable err ) {}
}
if ( headers.getPacketLength( ) > 0 ) {
headers.parsePacket( "8859_1" );
parseSocketInfo( headers, req );
req.parseHeaders( Arrays.asList( headers.getHeaders( ) ) );
String servletURI = parseURILine( headers, req, rsp );
req.setAttribute( TEMPORARY_URL_STASH, servletURI );
// If content-length present and non-zero, download the other
// packets
WinstoneInputStream inData = null;
int contentLength = req.getContentLength( );
if ( contentLength > 0 ) {
byte bodyContent[] = new byte[contentLength];
int position = 0;
while ( position < contentLength ) {
outSocket.write( getBodyRequestPacket( Math.min( contentLength
- position, 8184 ) ) );
position = getBodyResponsePacket( inSocket, bodyContent,
position );
Logger.log( Logger.FULL_DEBUG, AJP_RESOURCES,
"Ajp13Listener.ReadBodyProgress", new String[] {
"" + position, "" + contentLength } );
}
inData = new WinstoneInputStream( bodyContent );
inData.setContentLength( contentLength );
} else
inData = new WinstoneInputStream( new byte[0] );
req.setInputStream( inData );
// Build input/output streams, plus request/response
WinstoneOutputStream outData = new Ajp13OutputStream( socket
.getOutputStream( ), "8859_1" );
outData.setResponse( rsp );
rsp.setOutputStream( outData );
// Set the handler's member variables so it can execute the servlet
handler.setRequest( req );
handler.setResponse( rsp );
handler.setInStream( inData );
handler.setOutStream( outData );
}
}
/**
* Called by the request handler thread, because it needs specific shutdown
* code for this connection's protocol ( ie releasing input/output streams,
* etc ).
*/
226 public void deallocateRequestResponse( RequestHandlerThread handler,
227 WinstoneRequest req, WinstoneResponse rsp,
228 WinstoneInputStream inData, WinstoneOutputStream outData )
throws IOException {
handler.setInStream( null );
handler.setOutStream( null );
handler.setRequest( null );
handler.setResponse( null );
if ( req != null )
this.objectPool.releaseRequestToPool( req );
if ( rsp != null )
this.objectPool.releaseResponseToPool( rsp );
}
/**
* This is kind of a hack, since we have already parsed the uri to get the
* input stream. Just pass back the request uri
*/
244 public String parseURI( RequestHandlerThread handler, WinstoneRequest req,
245 WinstoneResponse rsp, WinstoneInputStream inData, Socket socket,
boolean iAmFirst ) throws IOException {
String uri = ( String ) req.getAttribute( TEMPORARY_URL_STASH );
req.removeAttribute( TEMPORARY_URL_STASH );
return uri;
}
/**
* Called by the request handler thread, because it needs specific shutdown
* code for this connection's protocol if the keep-alive period expires ( ie
* closing sockets, etc ).
*
* This implementation simply shuts down the socket and streams.
*/
259 public void releaseSocket( Socket socket, InputStream inSocket,
260 OutputStream outSocket ) throws IOException {
// Logger.log( Logger.FULL_DEBUG, "Releasing socket: " +
// Thread.currentThread( ).getName( ) );
inSocket.close( );
outSocket.close( );
socket.close( );
}
/**
* Extract the header details relating to socket stuff from the ajp13 header
* packet
*/
272 private void parseSocketInfo( Ajp13IncomingPacket headers,
273 WinstoneRequest req ) {
req.setServerPort( headers.getServerPort( ) );
req.setRemoteIP( headers.getRemoteAddress( ) );
req.setServerName( headers.getServerName( ) );
req.setLocalPort( headers.getServerPort( ) );
req.setLocalAddr( headers.getServerName( ) );
req.setRemoteIP( headers.getRemoteAddress( ) );
if ( ( headers.getRemoteHost( ) != null )
&& !headers.getRemoteHost( ).equals( "" ) )
req.setRemoteName( headers.getRemoteHost( ) );
else
req.setRemoteName( headers.getRemoteAddress( ) );
req.setScheme( headers.isSSL( ) ? "https" : "http" );
req.setIsSecure( headers.isSSL( ) );
}
/**
* Extract the header details relating to protocol, uri, etc from the ajp13
* header packet
*/
293 private String parseURILine( Ajp13IncomingPacket headers,
294 WinstoneRequest req, WinstoneResponse rsp )
throws UnsupportedEncodingException {
req.setMethod( headers.getMethod( ) );
req.setProtocol( headers.getProtocol( ) );
rsp.setProtocol( headers.getProtocol( ) );
rsp.extractRequestKeepAliveHeader( req );
// req.setServletPath( headers.getURI( ) );
// req.setRequestURI( headers.getURI( ) );
// Get query string if supplied
for ( Iterator i = headers.getAttributes( ).keySet( ).iterator( ); i
.hasNext( ); ) {
String attName = ( String ) i.next( );
if ( attName.equals( "query_string" ) ) {
String qs = ( String ) headers.getAttributes( ).get( "query_string" );
req.setQueryString( qs );
// req.getParameters( ).putAll( WinstoneRequest.extractParameters( qs,
// req.getEncoding( ), mainResources ) );
// req.setRequestURI( headers.getURI( ) + "?" + qs );
} else if ( attName.equals( "ssl_cert" ) ) {
String certValue = ( String ) headers.getAttributes( ).get(
"ssl_cert" );
InputStream certStream = new ByteArrayInputStream( certValue
.getBytes( "8859_1" ) );
X509Certificate certificateArray[] = new X509Certificate[1];
try {
certificateArray[0] = ( X509Certificate ) CertificateFactory
.getInstance( "X.509" ).generateCertificate(
certStream );
} catch ( CertificateException err ) {
Logger.log( Logger.DEBUG, AJP_RESOURCES,
"Ajp13Listener.SkippingCert", certValue );
}
req.setAttribute( "javax.servlet.request.X509Certificate",
certificateArray );
req.setIsSecure( true );
} else if ( attName.equals( "ssl_cipher" ) ) {
String cipher = ( String ) headers.getAttributes( ).get(
"ssl_cipher" );
req.setAttribute( "javax.servlet.request.cipher_suite", cipher );
req.setAttribute( "javax.servlet.request.key_size",
getKeySize( cipher ) );
req.setIsSecure( true );
} else if ( attName.equals( "ssl_session" ) ) {
req.setAttribute( "javax.servlet.request.ssl_session", headers
.getAttributes( ).get( "ssl_session" ) );
req.setIsSecure( true );
} else
Logger.log( Logger.DEBUG, AJP_RESOURCES,
"Ajp13Listener.UnknownAttribute", new String[] {
attName,
"" + headers.getAttributes( ).get( attName ) } );
}
return headers.getURI( );
}
351 private Integer getKeySize( String cipherSuite ) {
if ( cipherSuite.indexOf( "_WITH_NULL_" ) != -1 )
return new Integer( 0 );
else if ( cipherSuite.indexOf( "_WITH_IDEA_CBC_" ) != -1 )
return new Integer( 128 );
else if ( cipherSuite.indexOf( "_WITH_RC2_CBC_40_" ) != -1 )
return new Integer( 40 );
else if ( cipherSuite.indexOf( "_WITH_RC4_40_" ) != -1 )
return new Integer( 40 );
else if ( cipherSuite.indexOf( "_WITH_RC4_128_" ) != -1 )
return new Integer( 128 );
else if ( cipherSuite.indexOf( "_WITH_DES40_CBC_" ) != -1 )
return new Integer( 40 );
else if ( cipherSuite.indexOf( "_WITH_DES_CBC_" ) != -1 )
return new Integer( 56 );
else if ( cipherSuite.indexOf( "_WITH_3DES_EDE_CBC_" ) != -1 )
return new Integer( 168 );
else
return null;
}
/**
* Tries to wait for extra requests on the same socket. If any are found
* before the timeout expires, it exits with a true, indicating a new
* request is waiting. If the timeout expires, return a false, instructing
* the handler thread to begin shutting down the socket and relase itself.
*/
378 public boolean processKeepAlive( WinstoneRequest request,
379 WinstoneResponse response, InputStream inSocket )
throws IOException, InterruptedException {
return true;
}
/**
* Build the packet needed for asking for a body chunk
*/
387 private byte[] getBodyRequestPacket( int desiredPacketLength ) {
byte getBodyRequestPacket[] = new byte[] { 0x41, 0x42, 0x00, 0x03,
0x06, 0x00, 0x00 };
Ajp13OutputStream.setIntBlock( desiredPacketLength,
getBodyRequestPacket, 5 );
return getBodyRequestPacket;
}
/**
* Process the server response to a get_body_chunk request. This loads the
* packet from the stream, and unpacks it into the buffer at the right
* place.
*/
400 private int getBodyResponsePacket( InputStream in, byte buffer[], int offset )
throws IOException {
// Get the incoming packet flag
byte headerBuffer[] = new byte[4];
int headerBytesRead = in.read( headerBuffer );
if ( headerBytesRead != 4 )
throw new WinstoneException( AJP_RESOURCES
.getString( "Ajp13Listener.InvalidHeader" ) );
else if ( ( headerBuffer[0] != 0x12 ) || ( headerBuffer[1] != 0x34 ) )
throw new WinstoneException( AJP_RESOURCES
.getString( "Ajp13Listener.InvalidHeader" ) );
// Read in the whole packet
int packetLength = ( ( headerBuffer[2] & 0xFF ) << 8 )
+ ( headerBuffer[3] & 0xFF );
if ( packetLength == 0 )
return offset;
// Look for packet length
byte bodyLengthBuffer[] = new byte[2];
in.read( bodyLengthBuffer );
int bodyLength = ( ( bodyLengthBuffer[0] & 0xFF ) << 8 )
+ ( bodyLengthBuffer[1] & 0xFF );
int packetBytesRead = in.read( buffer, offset, bodyLength );
if ( packetBytesRead < bodyLength )
throw new WinstoneException( AJP_RESOURCES
.getString( "Ajp13Listener.ShortPacket" ) );
else
return packetBytesRead + offset;
}
//
// /**
// * Useful method for dumping out the contents of a packet in hex form
// */
// public static void packetDump( byte packetBytes[], int packetLength ) {
// String dump = "";
// for ( int n = 0; n < packetLength; n+=16 ) {
// String line = Integer.toHexString( ( n >> 4 ) & 0xF ) + "0:";
// for ( int j = 0; j < Math.min( packetLength - n, 16 ); j++ )
// line = line + " " + ( ( packetBytes[n + j] & 0xFF ) < 16 ? "0" : "" ) +
// Integer.toHexString( packetBytes[n + j] & 0xFF );
//
// line = line + " ";
// for ( int j = 0; j < Math.min( packetLength - n, 16 ); j++ ) {
// byte me = ( byte ) ( packetBytes[n + j] & 0xFF );
// line = line + ( ( ( me > 32 ) && ( me < 123 ) ) ? ( char ) me : '.' );
// }
// dump = dump + line + "\r\n";
// }
// System.out.println( dump );
// }
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.ajp13;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.Cookie;
import winstone.Logger;
import winstone.WinstoneException;
import winstone.WinstoneOutputStream;
/**
* Extends the winstone output stream, so that the ajp13 protocol requirements
* can be fulfilled.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Ajp13OutputStream.java, v 1.7 2007/05/05 00:52:50 rickknowles Exp $
*/
30 public class Ajp13OutputStream extends WinstoneOutputStream {
// Container originated packet types
byte CONTAINER_SEND_BODY_CHUNK = 0x03;
byte CONTAINER_SEND_HEADERS = 0x04;
byte CONTAINER_END_RESPONSE = 0x05;
// byte CONTAINER_GET_BODY_CHUNK = 0x06;
// byte CONTAINER_CPONG_REPLY = 0x09;
39 static Map headerCodes = null;
static {
headerCodes = new Hashtable( );
headerCodes.put( "content-type", new byte[] { ( byte ) 0xA0, 0x01 } );
headerCodes.put( "content-language", new byte[] { ( byte ) 0xA0, 0x02 } );
headerCodes.put( "content-length", new byte[] { ( byte ) 0xA0, 0x03 } );
headerCodes.put( "date", new byte[] { ( byte ) 0xA0, 0x04 } );
headerCodes.put( "last-modified", new byte[] { ( byte ) 0xA0, 0x05 } );
headerCodes.put( "location", new byte[] { ( byte ) 0xA0, 0x06 } );
headerCodes.put( "set-cookie", new byte[] { ( byte ) 0xA0, 0x07 } );
headerCodes.put( "set-cookie2", new byte[] { ( byte ) 0xA0, 0x08 } );
headerCodes.put( "servlet-engine", new byte[] { ( byte ) 0xA0, 0x09 } );
headerCodes.put( "server", new byte[] { ( byte ) 0xA0, 0x09 } );
headerCodes.put( "status", new byte[] { ( byte ) 0xA0, 0x0A } );
headerCodes.put( "www-authenticate", new byte[] { ( byte ) 0xA0, 0x0B } );
}
57 private String headerEncoding;
59 public Ajp13OutputStream( OutputStream outStream, String headerEncoding ) {
super( outStream, false );
this.headerEncoding = headerEncoding;
}
64 public void commit( ) throws IOException {
Logger.log( Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
"Ajp13OutputStream.CommittedBytes", "" + this.bytesCommitted );
this.buffer.flush( );
// If we haven't written the headers yet, write them out
if ( !this.committed ) {
this.owner.validateHeaders( );
this.committed = true;
ByteArrayOutputStream headerArrayStream = new ByteArrayOutputStream( );
for ( Iterator i = this.owner.getHeaders( ).iterator( ); i.hasNext( ); ) {
String header = ( String ) i.next( );
int colonPos = header.indexOf( ':' );
if ( colonPos == -1 )
throw new WinstoneException( Ajp13Listener.AJP_RESOURCES.getString(
"Ajp13OutputStream.NoColonHeader", header ) );
String headerName = header.substring( 0, colonPos ).trim( );
String headerValue = header.substring( colonPos + 1 ).trim( );
byte headerCode[] = ( byte[] ) headerCodes.get( headerName
.toLowerCase( ) );
if ( headerCode == null ) {
headerArrayStream.write( getStringBlock( headerName ) );
} else {
headerArrayStream.write( headerCode );
}
headerArrayStream.write( getStringBlock( headerValue ) );
}
for ( Iterator i = this.owner.getCookies( ).iterator( ); i.hasNext( ); ) {
Cookie cookie = ( Cookie ) i.next( );
String cookieText = this.owner.writeCookie( cookie );
int colonPos = cookieText.indexOf( ':' );
if ( colonPos == -1 )
throw new WinstoneException( Ajp13Listener.AJP_RESOURCES.getString(
"Ajp13OutputStream.NoColonHeader", cookieText ) );
String headerName = cookieText.substring( 0, colonPos ).trim( );
String headerValue = cookieText.substring( colonPos + 1 ).trim( );
byte headerCode[] = ( byte[] ) headerCodes.get( headerName.toLowerCase( ) );
if ( headerCode == null ) {
headerArrayStream.write( getStringBlock( headerName ) );
} else {
headerArrayStream.write( headerCode );
}
headerArrayStream.write( getStringBlock( headerValue ) );
}
// Write packet header + prefix + status code + status msg + header
// count
byte headerArray[] = headerArrayStream.toByteArray( );
byte headerPacket[] = new byte[12];
headerPacket[0] = ( byte ) 0x41;
headerPacket[1] = ( byte ) 0x42;
setIntBlock( headerArray.length + 8, headerPacket, 2 );
headerPacket[4] = CONTAINER_SEND_HEADERS;
setIntBlock( this.owner.getStatus( ), headerPacket, 5 );
setIntBlock( 0, headerPacket, 7 ); // empty msg
headerPacket[9] = ( byte ) 0x00;
setIntBlock( this.owner.getHeaders( ).size( )
+ this.owner.getCookies( ).size( ), headerPacket, 10 );
// Ajp13Listener.packetDump( headerPacket, headerPacket.length );
// Ajp13Listener.packetDump( headerArray, headerArray.length );
this.outStream.write( headerPacket );
this.outStream.write( headerArray );
}
// Write out the contents of the buffer in max 8k chunks
byte bufferContents[] = this.buffer.toByteArray( );
int position = 0;
while ( position < bufferContents.length ) {
int packetLength = Math.min( bufferContents.length - position, 8184 );
byte responsePacket[] = new byte[packetLength + 8];
responsePacket[0] = 0x41;
responsePacket[1] = 0x42;
setIntBlock( packetLength + 4, responsePacket, 2 );
responsePacket[4] = CONTAINER_SEND_BODY_CHUNK;
setIntBlock( packetLength, responsePacket, 5 );
System.arraycopy( bufferContents, position, responsePacket, 7, packetLength );
responsePacket[packetLength + 7] = 0x00;
position += packetLength;
// Ajp13Listener.packetDump( responsePacket, responsePacket.length );
this.outStream.write( responsePacket );
}
this.buffer.reset( );
this.bufferPosition = 0;
}
155 public void finishResponse( ) throws IOException {
// Send end response packet
byte endResponse[] = new byte[] { 0x41, 0x42, 0x00, 0x02,
CONTAINER_END_RESPONSE, 1 };
// Ajp13Listener.packetDump( endResponse, endResponse.length );
this.outStream.write( endResponse );
}
/**
* Useful generic method for getting ajp13 format integers in a packet.
*/
166 public byte[] getIntBlock( int integer ) {
byte hi = ( byte ) ( 0xFF & ( integer >> 8 ) );
byte lo = ( byte ) ( 0xFF & ( integer - ( hi << 8 ) ) );
return new byte[] { hi, lo };
}
/**
* Useful generic method for setting ajp13 format integers in a packet.
*/
175 public static void setIntBlock( int integer, byte packet[], int offset ) {
byte hi = ( byte ) ( 0xFF & ( integer >> 8 ) );
byte lo = ( byte ) ( 0xFF & ( integer - ( hi << 8 ) ) );
packet[offset] = hi;
packet[offset + 1] = lo;
}
/**
* Useful generic method for getting ajp13 format strings in a packet.
*/
185 public byte[] getStringBlock( String text )
throws UnsupportedEncodingException {
byte textBytes[] = text.getBytes( headerEncoding );
byte outArray[] = new byte[textBytes.length + 3];
System.arraycopy( getIntBlock( textBytes.length ), 0, outArray, 0, 2 );
System.arraycopy( textBytes, 0, outArray, 2, textBytes.length );
outArray[textBytes.length + 2] = 0x00;
return outArray;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
import winstone.AuthenticationHandler;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
/**
* Base class for managers of authentication within Winstone. This class also
* acts as a factory, loading the appropriate subclass for the requested auth
* type.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: BaseAuthenticationHandler.java, v 1.6 2006/02/28 07:32:47 rickknowles Exp $
*/
35 public abstract class BaseAuthenticationHandler implements
AuthenticationHandler {
37 static final String ELEM_REALM_NAME = "realm-name";
39 protected SecurityConstraint constraints[];
40 protected AuthenticationRealm realm;
41 protected String realmName;
42 public final static WinstoneResourceBundle AUTH_RESOURCES = new WinstoneResourceBundle( "winstone.auth.LocalStrings" );
/**
* Factory method - this parses the web.xml nodes and builds the correct
* subclass for handling that auth type.
*/
48 protected BaseAuthenticationHandler( Node loginConfigNode,
49 List constraintNodes, Set rolesAllowed,
50 AuthenticationRealm realm ) {
this.realm = realm;
for ( int m = 0; m < loginConfigNode.getChildNodes( ).getLength( ); m++ ) {
Node loginElm = loginConfigNode.getChildNodes( ).item( m );
if ( loginElm.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( loginElm.getNodeName( ).equals( ELEM_REALM_NAME ) )
realmName = WebAppConfiguration.getTextFromNode( loginElm );
}
// Build security constraints
this.constraints = new SecurityConstraint[constraintNodes.size( )];
for ( int n = 0; n < constraints.length; n++ )
this.constraints[n] = new SecurityConstraint( ( Node ) constraintNodes
.get( n ), rolesAllowed, n );
}
/**
* Evaluates any authentication constraints, intercepting if auth is
* required. The relevant authentication handler subclass's logic is used to
* actually authenticate.
*
* @return A boolean indicating whether to continue after this request
*/
75 public boolean processAuthentication( ServletRequest inRequest,
76 ServletResponse inResponse, String pathRequested )
throws IOException, ServletException {
Logger.log( Logger.FULL_DEBUG, AUTH_RESOURCES,
"BaseAuthenticationHandler.StartAuthCheck" );
HttpServletRequest request = ( HttpServletRequest ) inRequest;
HttpServletResponse response = ( HttpServletResponse ) inResponse;
// Give previous attempts a chance to be validated
if ( !validatePossibleAuthenticationResponse( request, response, pathRequested ) ) {
return false;
} else {
return doRoleCheck( request, response, pathRequested );
}
}
92 protected boolean doRoleCheck( HttpServletRequest request,
93 HttpServletResponse response, String pathRequested )
throws IOException, ServletException {
// Loop through constraints
boolean foundApplicable = false;
for ( int n = 0; ( n < this.constraints.length ) && !foundApplicable; n++ ) {
Logger.log( Logger.FULL_DEBUG, AUTH_RESOURCES,
"BaseAuthenticationHandler.EvalConstraint",
this.constraints[n].getName( ) );
// Find one that applies, then
if ( this.constraints[n].isApplicable( pathRequested, request.getMethod( ) ) ) {
Logger.log( Logger.FULL_DEBUG, AUTH_RESOURCES,
"BaseAuthenticationHandler.ApplicableConstraint",
this.constraints[n].getName( ) );
foundApplicable = true;
if ( this.constraints[n].needsSSL( ) && !request.isSecure( ) ) {
Logger.log( Logger.DEBUG, AUTH_RESOURCES,
"BaseAuthenticationHandler.ConstraintNeedsSSL",
this.constraints[n].getName( ) );
response.sendError( HttpServletResponse.SC_FORBIDDEN,
AUTH_RESOURCES.getString( "BaseAuthenticationHandler.ConstraintNeedsSSL",
this.constraints[n].getName( ) ) );
return false;
}
else if ( !this.constraints[n].isAllowed( request ) ) {
// Logger.log( Logger.FULL_DEBUG, "Not allowed - requesting auth" );
requestAuthentication( request, response, pathRequested );
return false;
} else {
// Logger.log( Logger.FULL_DEBUG, "Allowed - authorization accepted" );
// Ensure that secured resources are not cached
setNoCache( response );
}
}
}
// If we made it this far without a check being run, there must be none applicable
Logger.log( Logger.FULL_DEBUG, AUTH_RESOURCES, "BaseAuthenticationHandler.PassedAuthCheck" );
return true;
}
136 protected void setNoCache( HttpServletResponse response ) {
response.setHeader( "Pragma", "No-cache" );
response.setHeader( "Cache-Control", "No-cache" );
response.setDateHeader( "Expires", 1 );
}
/**
* The actual auth request implementation.
*/
145 protected abstract void requestAuthentication( HttpServletRequest request,
146 HttpServletResponse response, String pathRequested )
throws IOException, ServletException;
/**
* Handling the ( possible ) response
*/
152 protected abstract boolean validatePossibleAuthenticationResponse(
153 HttpServletRequest request, HttpServletResponse response,
154 String pathRequested ) throws ServletException, IOException;
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
import winstone.AuthenticationPrincipal;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WinstoneRequest;
/**
* Handles HTTP basic authentication.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: BasicAuthenticationHandler.java, v 1.5 2007/04/11 13:14:26 rickknowles Exp $
*/
30 public class BasicAuthenticationHandler extends BaseAuthenticationHandler {
31 public BasicAuthenticationHandler( Node loginConfigNode,
32 List constraintNodes, Set rolesAllowed,
33 AuthenticationRealm realm ) {
super( loginConfigNode, constraintNodes, rolesAllowed, realm );
Logger.log( Logger.DEBUG, AUTH_RESOURCES,
"BasicAuthenticationHandler.Initialised", realmName );
}
/**
* Call this once we know that we need to authenticate
*/
42 protected void requestAuthentication( HttpServletRequest request,
43 HttpServletResponse response, String pathRequested )
throws IOException {
// Return unauthorized, and set the realm name
response.setHeader( "WWW-Authenticate", "Basic Realm=\""
+ this.realmName + "\"" );
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, AUTH_RESOURCES
.getString( "BasicAuthenticationHandler.UnauthorizedMessage" ) );
}
/**
* Handling the ( possible ) response
*/
55 protected boolean validatePossibleAuthenticationResponse(
56 HttpServletRequest request, HttpServletResponse response,
57 String pathRequested ) throws IOException {
String authorization = request.getHeader( "Authorization" );
if ( ( authorization != null )
&& authorization.toLowerCase( ).startsWith( "basic" ) ) {
char[] inBytes = authorization.substring( 5 ).trim( ).toCharArray( );
byte[] outBytes = new byte[( int ) ( inBytes.length * 0.75f )]; // always mod 4 = 0
int length = decodeBase64( inBytes, outBytes, 0, inBytes.length, 0 );
String decoded = new String( outBytes, 0, length );
int delimPos = decoded.indexOf( ':' );
if ( delimPos != -1 ) {
AuthenticationPrincipal principal = this.realm
.authenticateByUsernamePassword( decoded.substring( 0,
delimPos ).trim( ), decoded.substring(
delimPos + 1 ).trim( ) );
if ( principal != null ) {
principal.setAuthType( HttpServletRequest.BASIC_AUTH );
if ( request instanceof WinstoneRequest )
( ( WinstoneRequest ) request ).setRemoteUser( principal );
else if ( request instanceof HttpServletRequestWrapper ) {
HttpServletRequestWrapper wrapper = ( HttpServletRequestWrapper ) request;
if ( wrapper.getRequest( ) instanceof WinstoneRequest )
( ( WinstoneRequest ) wrapper.getRequest( ) )
.setRemoteUser( principal );
else
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"BasicAuthenticationHandler.CantSetUser",
wrapper.getRequest( ).getClass( ).getName( ) );
} else
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"BasicAuthenticationHandler.CantSetUser",
request.getClass( ).getName( ) );
}
}
}
return true;
}
/**
* Decodes a byte array from base64
*/
99 public static int decodeBase64( char[] input, byte[] output,
int inOffset, int inLength, int outOffset ) {
if ( inLength == 0 ) {
return 0;
}
int outIndex = outOffset;
for ( int inIndex = inOffset; inIndex < inLength; ) {
// Decode four bytes
int thisPassInBytes = Math.min( inLength - inIndex, 4 );
while ( ( thisPassInBytes > 1 ) &&
( input[inIndex + thisPassInBytes - 1] == '=' ) ) {
thisPassInBytes--;
}
if ( thisPassInBytes == 2 ) {
int outBuffer = ( ( B64_DECODE_ARRAY[input[inIndex]] & 0xFF ) << 18 )
| ( ( B64_DECODE_ARRAY[input[inIndex + 1]] & 0xFF ) << 12 );
output[outIndex] = ( byte ) ( ( outBuffer >> 16 ) & 0xFF );
outIndex += 1;
} else if ( thisPassInBytes == 3 ) {
int outBuffer = ( ( B64_DECODE_ARRAY[input[inIndex]] & 0xFF ) << 18 )
| ( ( B64_DECODE_ARRAY[input[inIndex + 1]] & 0xFF ) << 12 )
| ( ( B64_DECODE_ARRAY[input[inIndex + 2]] & 0xFF ) << 6 );
output[outIndex] = ( byte ) ( ( outBuffer >> 16 ) & 0xFF );
output[outIndex + 1] = ( byte ) ( ( outBuffer >> 8 ) & 0xFF );
outIndex += 2;
} else if ( thisPassInBytes == 4 ) {
int outBuffer = ( ( B64_DECODE_ARRAY[input[inIndex]] & 0xFF ) << 18 )
| ( ( B64_DECODE_ARRAY[input[inIndex + 1]] & 0xFF ) << 12 )
| ( ( B64_DECODE_ARRAY[input[inIndex + 2]] & 0xFF ) << 6 )
| ( B64_DECODE_ARRAY[input[inIndex + 3]] & 0xFF );
output[outIndex] = ( byte ) ( ( outBuffer >> 16 ) & 0xFF );
output[outIndex + 1] = ( byte ) ( ( outBuffer >> 8 ) & 0xFF );
output[outIndex + 2] = ( byte ) ( outBuffer & 0xFF );
outIndex += 3;
}
inIndex += thisPassInBytes;
}
return outIndex;
}
private static byte B64_DECODE_ARRAY[] = new byte[] { -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, 62, // Plus sign
-1, -1, -1, 63, // Slash
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers
-1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Large letters
-1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Small letters
-1, -1, -1, -1 };
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
import winstone.AuthenticationPrincipal;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WinstoneRequest;
/**
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ClientcertAuthenticationHandler.java, v 1.3 2006/02/28 07:32:47 rickknowles Exp $
*/
29 public class ClientcertAuthenticationHandler extends BaseAuthenticationHandler {
30 public ClientcertAuthenticationHandler( Node loginConfigNode,
31 List constraintNodes, Set rolesAllowed,
32 AuthenticationRealm realm ) {
super( loginConfigNode, constraintNodes, rolesAllowed, realm );
Logger.log( Logger.DEBUG, AUTH_RESOURCES,
"ClientcertAuthenticationHandler.Initialised", realmName );
}
/**
* Call this once we know that we need to authenticate
*/
41 protected void requestAuthentication( HttpServletRequest request,
42 HttpServletResponse response, String pathRequested )
throws IOException {
// Return unauthorized, and set the realm name
response.sendError( HttpServletResponse.SC_UNAUTHORIZED,
AUTH_RESOURCES.getString( "ClientcertAuthenticationHandler.UnauthorizedMessage" ) );
}
/**
* Handling the ( possible ) response
*/
52 protected boolean validatePossibleAuthenticationResponse(
53 HttpServletRequest request, HttpServletResponse response,
54 String pathRequested ) throws IOException {
// Check for certificates in the request attributes
X509Certificate certificateArray[] = ( X509Certificate[] ) request
.getAttribute( "javax.servlet.request.X509Certificate" );
if ( ( certificateArray != null ) && ( certificateArray.length > 0 ) ) {
boolean failed = false;
for ( int n = 0; n < certificateArray.length; n++ )
try {
certificateArray[n].checkValidity( );
} catch ( Throwable err ) {
failed = true;
}
if ( !failed ) {
AuthenticationPrincipal principal = this.realm
.retrieveUser( certificateArray[0].getSubjectDN( )
.getName( ) );
if ( principal != null ) {
principal.setAuthType( HttpServletRequest.CLIENT_CERT_AUTH );
if ( request instanceof WinstoneRequest )
( ( WinstoneRequest ) request ).setRemoteUser( principal );
else if ( request instanceof HttpServletRequestWrapper ) {
HttpServletRequestWrapper wrapper = ( HttpServletRequestWrapper ) request;
if ( wrapper.getRequest( ) instanceof WinstoneRequest )
( ( WinstoneRequest ) wrapper.getRequest( ) )
.setRemoteUser( principal );
else
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"ClientCertAuthenticationHandler.CantSetUser",
wrapper.getRequest( ).getClass( ).getName( ) );
} else
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"ClientCertAuthenticationHandler.CantSetUser",
request.getClass( ).getName( ) );
}
}
}
return true;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
import winstone.AuthenticationPrincipal;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WinstoneRequest;
import winstone.WinstoneResourceBundle;
/**
* Implements the MD5 digest version of authentication
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: DigestAuthenticationHandler.java, v 1.3 2004/05/22 06:53:45
* rickknowles Exp $
*/
37 public class DigestAuthenticationHandler extends BaseAuthenticationHandler {
38 private MessageDigest md5Digester;
40 public DigestAuthenticationHandler( Node loginConfigNode,
41 List constraintNodes, Set rolesAllowed,
42 AuthenticationRealm realm ) throws NoSuchAlgorithmException {
super( loginConfigNode, constraintNodes, rolesAllowed, realm );
this.md5Digester = MessageDigest.getInstance( "MD5" );
Logger.log( Logger.DEBUG, AUTH_RESOURCES,
"DigestAuthenticationHandler.Initialised", realmName );
}
/**
* Call this once we know that we need to authenticate
*/
52 protected void requestAuthentication( HttpServletRequest request,
53 HttpServletResponse response, String pathRequested )
throws IOException {
// Generate the one time token
String oneTimeToken = "WinstoneToken:"
+ ( new Random( ).nextDouble( ) * System.currentTimeMillis( ) );
// Need to write the www-authenticate header
String authHeader = "Digest realm=\"" + this.realmName
+ "\", qop=\"auth\", " + "nonce=\"" + oneTimeToken
+ "\", opaque=\"" + md5Encode( oneTimeToken ) + "\"";
response.setHeader( "WWW-Authenticate", authHeader );
// Return unauthorized
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, AUTH_RESOURCES
.getString( "DigestAuthenticationHandler.UnauthorizedMessage" ) );
}
/**
* Handling the ( possible ) response
*
* @return True if the request should continue, or false if we have
* intercepted it
*/
76 protected boolean validatePossibleAuthenticationResponse(
77 HttpServletRequest request, HttpServletResponse response,
78 String pathRequested ) throws IOException {
String authorization = request.getHeader( "Authorization" );
if ( authorization == null )
return true;
// Logger.log( Logger.FULL_DEBUG, "Authorization: " + authorization );
if ( !authorization.startsWith( "Digest" ) )
return true;
// Extract tokens from auth string
String userName = null;
String realm = null;
String qop = null;
String algorithm = null;
String uri = null;
String nOnce = null;
String nc = null;
String cnOnce = null;
String clientResponseDigest = null;
StringTokenizer st = new StringTokenizer( authorization.substring( 6 )
.trim( ), ", " );
while ( st.hasMoreTokens( ) ) {
String token = st.nextToken( ).trim( );
int equalPos = token.indexOf( '=' );
String paramName = token.substring( 0, equalPos );
if ( paramName.equals( "username" ) )
userName = WinstoneResourceBundle.globalReplace( token
.substring( equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "realm" ) )
realm = WinstoneResourceBundle.globalReplace( token.substring(
equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "qop" ) )
qop = WinstoneResourceBundle.globalReplace( token.substring(
equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "algorithm" ) )
algorithm = WinstoneResourceBundle.globalReplace( token
.substring( equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "uri" ) )
uri = WinstoneResourceBundle.globalReplace( token.substring(
equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "nonce" ) )
nOnce = WinstoneResourceBundle.globalReplace( token.substring(
equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "nc" ) )
nc = WinstoneResourceBundle.globalReplace( token.substring(
equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "cnonce" ) )
cnOnce = WinstoneResourceBundle.globalReplace( token.substring(
equalPos + 1 ).trim( ), "\"", "" );
else if ( paramName.equals( "response" ) )
clientResponseDigest = WinstoneResourceBundle.globalReplace(
token.substring( equalPos + 1 ).trim( ), "\"", "" );
}
// Throw out bad attempts
if ( ( userName == null ) || ( realm == null ) || ( qop == null )
|| ( uri == null ) || ( nOnce == null ) || ( nc == null )
|| ( cnOnce == null ) || ( clientResponseDigest == null ) )
return true;
else if ( ( algorithm != null ) && !algorithm.equals( "MD5" ) )
return true;
// Get a user matching the username
AuthenticationPrincipal principal = this.realm.retrieveUser( userName );
if ( principal == null )
return true;
// Compute the 2 digests and compare
String userRealmPasswordDigest = md5Encode( userName + ":" + realm + ":"
+ principal.getPassword( ) );
String methodURIDigest = md5Encode( request.getMethod( ) + ":" + uri );
String serverResponseDigest = md5Encode( userRealmPasswordDigest + ":"
+ nOnce + ":" + nc + ":" + cnOnce + ":" + qop + ":"
+ methodURIDigest );
if ( serverResponseDigest.equals( clientResponseDigest ) ) {
principal.setAuthType( HttpServletRequest.DIGEST_AUTH );
if ( request instanceof WinstoneRequest )
( ( WinstoneRequest ) request ).setRemoteUser( principal );
else if ( request instanceof HttpServletRequestWrapper ) {
HttpServletRequestWrapper wrapper = ( HttpServletRequestWrapper ) request;
if ( wrapper.getRequest( ) instanceof WinstoneRequest )
( ( WinstoneRequest ) wrapper.getRequest( ) )
.setRemoteUser( principal );
else
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"DigestAuthenticationHandler.CantSetUser", wrapper
.getRequest( ).getClass( ).getName( ) );
} else
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"DigestAuthenticationHandler.CantSetUser", request
.getClass( ).getName( ) );
}
return true;
}
/**
* Returns a hex encoded MD5 digested version of the input string
* @param input The string to encode
* @return MD5 digested, hex encoded version of the input
*/
179 public String md5Encode( String input ) throws UnsupportedEncodingException {
// Digest
byte digestBytes[] = this.md5Digester.digest( input.getBytes( "8859_1" ) );
// Write out in hex format
char outArray[] = new char[32];
for ( int n = 0; n < digestBytes.length; n++ ) {
int hiNibble = ( digestBytes[n] & 0xFF ) >> 4;
int loNibble = ( digestBytes[n] & 0xF );
outArray[2 * n] = ( hiNibble > 9 ? ( char ) ( hiNibble + 87 )
: ( char ) ( hiNibble + 48 ) );
outArray[2 * n + 1] = ( loNibble > 9 ? ( char ) ( loNibble + 87 )
: ( char ) ( loNibble + 48 ) );
}
return new String( outArray );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.w3c.dom.Node;
import winstone.AuthenticationPrincipal;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneRequest;
/**
* Handles FORM based authentication configurations. Fairly simple ... it just
* redirects any unauthorized requests to the login page, and any bad logins to
* the error page. The auth values are stored in the session in a special slot.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: FormAuthenticationHandler.java, v 1.7 2006/12/13 14:07:43 rickknowles Exp $
*/
37 public class FormAuthenticationHandler extends BaseAuthenticationHandler {
38 private static final String ELEM_FORM_LOGIN_CONFIG = "form-login-config";
39 private static final String ELEM_FORM_LOGIN_PAGE = "form-login-page";
40 private static final String ELEM_FORM_ERROR_PAGE = "form-error-page";
41 private static final String FORM_ACTION = "j_security_check";
42 private static final String FORM_USER = "j_username";
43 private static final String FORM_PASS = "j_password";
44 private static final String AUTHENTICATED_USER = "winstone.auth.FormAuthenticationHandler.AUTHENTICATED_USER";
45 private static final String CACHED_REQUEST = "winstone.auth.FormAuthenticationHandler.CACHED_REQUEST";
47 private String loginPage;
48 private String errorPage;
/**
* Constructor for the FORM authenticator
*
* @param realm
* The realm against which we are authenticating
* @param constraints
* The array of security constraints that might apply
* @param resources
* The list of resource strings for messages
* @param realmName
* The name of the realm this handler claims
*/
62 public FormAuthenticationHandler( Node loginConfigNode,
63 List constraintNodes, Set rolesAllowed,
64 AuthenticationRealm realm ) {
super( loginConfigNode, constraintNodes, rolesAllowed, realm );
for ( int n = 0; n < loginConfigNode.getChildNodes( ).getLength( ); n++ ) {
Node loginElm = loginConfigNode.getChildNodes( ).item( n );
if ( loginElm.getNodeName( ).equals( ELEM_FORM_LOGIN_CONFIG ) ) {
for ( int k = 0; k < loginElm.getChildNodes( ).getLength( ); k++ ) {
Node formElm = loginElm.getChildNodes( ).item( k );
if ( formElm.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( formElm.getNodeName( ).equals( ELEM_FORM_LOGIN_PAGE ) )
loginPage = WebAppConfiguration.getTextFromNode( formElm );
else if ( formElm.getNodeName( ).equals( ELEM_FORM_ERROR_PAGE ) )
errorPage = WebAppConfiguration.getTextFromNode( formElm );
}
}
}
Logger.log( Logger.DEBUG, AUTH_RESOURCES,
"FormAuthenticationHandler.Initialised", realmName );
}
/**
* Evaluates any authentication constraints, intercepting if auth is
* required. The relevant authentication handler subclass's logic is used to
* actually authenticate.
*
* @return A boolean indicating whether to continue after this request
*/
92 public boolean processAuthentication( ServletRequest request,
93 ServletResponse response, String pathRequested ) throws IOException,
ServletException {
if ( pathRequested.equals( this.loginPage )
|| pathRequested.equals( this.errorPage ) ) {
return true;
} else {
return super.processAuthentication( request, response, pathRequested );
}
}
/**
* Call this once we know that we need to authenticate
*/
106 protected void requestAuthentication( HttpServletRequest request,
107 HttpServletResponse response, String pathRequested )
throws ServletException, IOException {
// Save the critical details of the request into the session map
ServletRequest unwrapped = request;
while ( unwrapped instanceof HttpServletRequestWrapper ) {
unwrapped = ( ( HttpServletRequestWrapper ) unwrapped ).getRequest( );
}
HttpSession session = request.getSession( true );
session.setAttribute( CACHED_REQUEST, new RetryRequestParams( unwrapped ) );
// Forward on to the login page
Logger.log( Logger.FULL_DEBUG, AUTH_RESOURCES,
"FormAuthenticationHandler.GoToLoginPage" );
javax.servlet.RequestDispatcher rdLogin = request
.getRequestDispatcher( this.loginPage );
setNoCache( response );
rdLogin.forward( request, response );
}
/**
* Check the response - is it a response to the login page ?
*
* @return A boolean indicating whether to continue with the request or not
*/
131 protected boolean validatePossibleAuthenticationResponse(
132 HttpServletRequest request, HttpServletResponse response,
133 String pathRequested ) throws ServletException, IOException {
// Check if this is a j_security_check uri
if ( pathRequested.endsWith( FORM_ACTION ) ) {
String username = request.getParameter( FORM_USER );
String password = request.getParameter( FORM_PASS );
// Send to error page if invalid
AuthenticationPrincipal principal = this.realm
.authenticateByUsernamePassword( username, password );
if ( principal == null ) {
javax.servlet.RequestDispatcher rdError = request
.getRequestDispatcher( this.errorPage );
rdError.forward( request, response );
}
// Send to stashed request
else {
// Iterate back as far as we can
ServletRequest wrapperCheck = request;
while ( wrapperCheck instanceof HttpServletRequestWrapper ) {
wrapperCheck = ( ( HttpServletRequestWrapper ) wrapperCheck ).getRequest( );
}
// Get the stashed request
WinstoneRequest actualRequest = null;
if ( wrapperCheck instanceof WinstoneRequest ) {
actualRequest = ( WinstoneRequest ) wrapperCheck;
actualRequest.setRemoteUser( principal );
} else {
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"FormAuthenticationHandler.CantSetUser",
wrapperCheck.getClass( ).getName( ) );
}
HttpSession session = request.getSession( true );
String previousLocation = this.loginPage;
RetryRequestParams cachedRequest = ( RetryRequestParams )
session.getAttribute( CACHED_REQUEST );
if ( ( cachedRequest != null ) && ( actualRequest != null ) ) {
// Repopulate this request from the params we saved
request = new RetryRequestWrapper( request, cachedRequest );
previousLocation =
( request.getServletPath( ) == null ? "" : request.getServletPath( ) ) +
( request.getPathInfo( ) == null ? "" : request.getPathInfo( ) );
} else {
Logger.log( Logger.DEBUG, AUTH_RESOURCES,
"FormAuthenticationHandler.NoCachedRequest" );
}
// do role check, since we don't know that this user has permission
if ( doRoleCheck( request, response, previousLocation ) ) {
principal.setAuthType( HttpServletRequest.FORM_AUTH );
session.setAttribute( AUTHENTICATED_USER, principal );
javax.servlet.RequestDispatcher rdPrevious = request
.getRequestDispatcher( previousLocation );
rdPrevious.forward( request, response );
} else {
javax.servlet.RequestDispatcher rdError = request
.getRequestDispatcher( this.errorPage );
rdError.forward( request, response );
}
}
return false;
}
// If it's not a login, get the session, and look up the auth user variable
else {
WinstoneRequest actualRequest = null;
if ( request instanceof WinstoneRequest ) {
actualRequest = ( WinstoneRequest ) request;
} else if ( request instanceof HttpServletRequestWrapper ) {
HttpServletRequestWrapper wrapper = ( HttpServletRequestWrapper ) request;
if ( wrapper.getRequest( ) instanceof WinstoneRequest ) {
actualRequest = ( WinstoneRequest ) wrapper.getRequest( );
} else {
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"FormAuthenticationHandler.CantSetUser", wrapper
.getRequest( ).getClass( ).getName( ) );
}
} else {
Logger.log( Logger.WARNING, AUTH_RESOURCES,
"FormAuthenticationHandler.CantSetUser", request
.getClass( ).getName( ) );
}
HttpSession session = actualRequest.getSession( false );
if ( session != null ) {
AuthenticationPrincipal authenticatedUser = ( AuthenticationPrincipal )
session.getAttribute( AUTHENTICATED_USER );
if ( authenticatedUser != null ) {
actualRequest.setRemoteUser( authenticatedUser );
Logger.log( Logger.FULL_DEBUG, AUTH_RESOURCES,
"FormAuthenticationHandler.GotUserFromSession" );
}
}
return true;
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* This is used by the ACL filter to allow a retry by using a key lookup
* on old request. It's only used when retrying an old request that was blocked
* by the ACL filter.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: RetryRequestParams.java, v 1.2 2007/06/01 15:59:53 rickknowles Exp $
*/
30 public class RetryRequestParams implements java.io.Serializable {
32 private String method;
33 private String scheme;
34 private String contextPath;
35 private String servletPath;
36 private String pathInfo;
37 private String queryString;
38 private String protocol;
private int contentLength;
40 private String contentType;
41 private String encoding;
42 private Map headers;
43 private Vector locales;
44 private Locale locale;
private byte[] bodyContent;
/**
* Constructor - this populates the wrapper from the object in session
*/
50 public RetryRequestParams( ServletRequest request ) throws IOException {
this.protocol = request.getProtocol( );
this.locales = new Vector( Collections.list( request.getLocales( ) ) );
this.locale = request.getLocale( );
this.contentLength = request.getContentLength( );
this.contentType = request.getContentType( );
this.encoding = request.getCharacterEncoding( );
this.headers = new HashMap( );
if ( request instanceof HttpServletRequest ) {
HttpServletRequest httpRequest = ( HttpServletRequest ) request;
this.method = httpRequest.getMethod( );
this.contextPath = httpRequest.getContextPath( );
this.servletPath = httpRequest.getServletPath( );
this.pathInfo = httpRequest.getPathInfo( );
this.queryString = httpRequest.getQueryString( );
for ( Enumeration names = httpRequest.getHeaderNames( ); names.hasMoreElements( ); ) {
String name = ( String ) names.nextElement( );
headers.put( name.toLowerCase( ), new Vector( Collections.list( httpRequest.getHeaders( name ) ) ) );
}
}
if ( ( ( this.method == null ) || this.method.equalsIgnoreCase( "POST" ) ) && ( this.contentLength != -1 ) ) {
InputStream inData = request.getInputStream( );
this.bodyContent = new byte[this.contentLength];
int readCount = 0;
int read = 0;
while ( ( read = inData.read( this.bodyContent, readCount, this.contentLength - readCount ) ) >= 0 ) {
readCount += read;
}
inData.close( );
}
}
85 public byte[] getBodyContent( ) {
return bodyContent;
}
89 public int getContentLength( ) {
return contentLength;
}
93 public String getContentType( ) {
return contentType;
}
97 public String getEncoding( ) {
return encoding;
}
101 public Map getHeaders( ) {
return headers;
}
105 public Locale getLocale( ) {
return locale;
}
109 public Vector getLocales( ) {
return locales;
}
113 public String getMethod( ) {
return method;
}
117 public String getPathInfo( ) {
return pathInfo;
}
121 public String getProtocol( ) {
return protocol;
}
125 public String getQueryString( ) {
return queryString;
}
129 public String getScheme( ) {
return scheme;
}
133 public String getServletPath( ) {
return servletPath;
}
137 public String getContextPath( ) {
return contextPath;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.Vector;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import winstone.Launcher;
import winstone.Logger;
import winstone.WinstoneException;
import winstone.WinstoneInputStream;
import winstone.WinstoneRequest;
/**
* This is used by the ACL filter to allow a retry by using a key lookup
* on old request. It's only used when retrying an old request that was blocked
* by the ACL filter.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: RetryRequestWrapper.java, v 1.3 2007/02/26 00:28:05 rickknowles Exp $
*/
43 public class RetryRequestWrapper extends HttpServletRequestWrapper {
44 protected static final DateFormat headerDF = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss z", Locale.US );
static {
headerDF.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
}
50 private final static String METHOD_HEAD = "GET";
51 private final static String METHOD_GET = "GET";
52 private final static String METHOD_POST = "POST";
53 private final static String POST_PARAMETERS = "application/x-www-form-urlencoded";
55 private RetryRequestParams oldRequest;
// PARAMETER/BODY RELATED FUNCTIONS
58 private String encoding;
59 private Map parsedParams;
60 private ServletInputStream inData;
/**
* Constructor - this populates the wrapper from the object in session
*/
65 public RetryRequestWrapper( HttpServletRequest request, RetryRequestParams oldRequest )
throws IOException {
super( request );
this.oldRequest = oldRequest;
this.encoding = this.oldRequest.getEncoding( );
}
72 private boolean hasBeenForwarded( ) {
return ( super.getAttribute( "javax.servlet.forward.request_uri" ) != null );
}
76 public String getScheme( ) {
if ( hasBeenForwarded( ) ) {
return super.getScheme( );
} else {
return this.oldRequest.getScheme( );
}
}
84 public String getMethod( ) {
if ( hasBeenForwarded( ) ) {
return super.getMethod( );
} else {
return this.oldRequest.getMethod( );
}
}
92 public String getContextPath( ) {
if ( hasBeenForwarded( ) ) {
return super.getContextPath( );
} else {
return this.oldRequest.getContextPath( );
}
}
100 public String getServletPath( ) {
if ( hasBeenForwarded( ) ) {
return super.getServletPath( );
} else {
return this.oldRequest.getServletPath( );
}
}
108 public String getPathInfo( ) {
if ( hasBeenForwarded( ) ) {
return super.getPathInfo( );
} else {
return this.oldRequest.getPathInfo( );
}
}
116 public String getQueryString( ) {
if ( hasBeenForwarded( ) ) {
return super.getQueryString( );
} else {
return this.oldRequest.getQueryString( );
}
}
124 public String getRequestURI( ) {
if ( hasBeenForwarded( ) ) {
return super.getRequestURI( );
} else {
String contextPath = this.oldRequest.getContextPath( );
String servletPath = this.oldRequest.getServletPath( );
String pathInfo = this.oldRequest.getPathInfo( );
String queryString = this.oldRequest.getQueryString( );
return contextPath + servletPath + ( ( pathInfo == null ) ? "" : pathInfo )
+ ( ( queryString == null ) ? "" : ( "?" + queryString ) );
}
}
137 public String getCharacterEncoding( ) {
if ( hasBeenForwarded( ) ) {
return super.getCharacterEncoding( );
} else {
return this.oldRequest.getEncoding( );
}
}
145 public void setCharacterEncoding( String encoding ) throws UnsupportedEncodingException {
if ( hasBeenForwarded( ) ) {
super.setCharacterEncoding( encoding );
} else {
this.encoding = encoding;
}
}
153 public int getContentLength( ) {
if ( hasBeenForwarded( ) ) {
return super.getContentLength( );
} else {
return this.oldRequest.getContentLength( );
}
}
161 public String getContentType( ) {
if ( hasBeenForwarded( ) ) {
return super.getContentType( );
} else {
return this.oldRequest.getContentType( );
}
}
169 public Locale getLocale( ) {
if ( hasBeenForwarded( ) ) {
return super.getLocale( );
} else {
return this.oldRequest.getLocale( );
}
}
177 public Enumeration getLocales( ) {
if ( hasBeenForwarded( ) ) {
return super.getLocales( );
} else {
return this.oldRequest.getLocales( ).elements( );
}
}
// -------------------------------------------------------------------
// HEADER RELATED FUNCTIONS
187 public long getDateHeader( String name ) {
if ( hasBeenForwarded( ) ) {
return super.getDateHeader( name );
} else {
String dateHeader = getHeader( name );
if ( dateHeader == null ) {
return -1;
} else {
try {
synchronized ( headerDF ) {
return headerDF.parse( dateHeader ).getTime( );
}
} catch ( java.text.ParseException err ) {
throw new IllegalArgumentException( "Illegal date format: " + dateHeader );
}
}
}
}
206 public int getIntHeader( String name ) {
if ( hasBeenForwarded( ) ) {
return super.getIntHeader( name );
} else {
String header = getHeader( name );
return header == null ? -1 : Integer.parseInt( header );
}
}
215 public String getHeader( String name ) {
if ( hasBeenForwarded( ) ) {
return super.getHeader( name );
} else {
Enumeration e = getHeaders( name );
return ( e != null ) && e.hasMoreElements( ) ? ( String ) e.nextElement( ) : null;
}
}
224 public Enumeration getHeaderNames( ) {
if ( hasBeenForwarded( ) ) {
return super.getHeaderNames( );
} else {
return Collections.enumeration( this.oldRequest.getHeaders( ).keySet( ) );
}
}
232 public Enumeration getHeaders( String name ) {
if ( hasBeenForwarded( ) ) {
return super.getHeaders( name );
} else {
Vector result = ( Vector ) this.oldRequest.getHeaders( ).get( name.toLowerCase( ) );
return result == null ? null : result.elements( );
}
}
241 public String getParameter( String name ) {
if ( hasBeenForwarded( ) ) {
return super.getParameter( name );
} else {
parseRequestParameters( );
Object param = this.parsedParams.get( name );
if ( param == null ) {
return null;
} else if ( param instanceof String ) {
return ( String ) param;
} else if ( param instanceof String[] ) {
return ( ( String[] ) param )[0];
} else {
return param.toString( );
}
}
}
259 public Enumeration getParameterNames( ) {
if ( hasBeenForwarded( ) ) {
return super.getParameterNames( );
} else {
parseRequestParameters( );
return Collections.enumeration( this.parsedParams.keySet( ) );
}
}
268 public String[] getParameterValues( String name ) {
if ( hasBeenForwarded( ) ) {
return super.getParameterValues( name );
} else {
parseRequestParameters( );
Object param = this.parsedParams.get( name );
if ( param == null ) {
return null;
} else if ( param instanceof String ) {
return new String[] {( String ) param};
} else if ( param instanceof String[] ) {
return ( String[] ) param;
} else {
throw new WinstoneException( Launcher.RESOURCES.getString(
"WinstoneRequest.UnknownParameterType", name + " - "
+ param.getClass( ) ) );
}
}
}
288 public Map getParameterMap( ) {
if ( hasBeenForwarded( ) ) {
return super.getParameterMap( );
} else {
Hashtable paramMap = new Hashtable( );
for ( Enumeration names = this.getParameterNames( ); names.hasMoreElements( ); ) {
String name = ( String ) names.nextElement( );
paramMap.put( name, getParameterValues( name ) );
}
return paramMap;
}
}
301 public BufferedReader getReader( ) throws IOException {
if ( hasBeenForwarded( ) ) {
return super.getReader( );
} else if ( getCharacterEncoding( ) != null ) {
return new BufferedReader( new InputStreamReader( getInputStream( ), this.encoding ) );
} else {
return new BufferedReader( new InputStreamReader( getInputStream( ) ) );
}
}
311 public ServletInputStream getInputStream( ) throws IOException {
if ( hasBeenForwarded( ) ) {
return super.getInputStream( );
} else if ( this.parsedParams != null ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WinstoneRequest.BothMethods" );
}
if ( this.inData == null ) {
this.inData = new WinstoneInputStream( this.oldRequest.getBodyContent( ) );
}
return this.inData;
}
// -------------------------------------------------------------------
/**
* This takes the parameters in the body of the request and puts them into
* the parameters map.
*/
331 private void parseRequestParameters( ) {
if ( inData != null ) {
Logger.log( Logger.WARNING, Launcher.RESOURCES, "WinstoneRequest.BothMethods" );
}
if ( this.parsedParams == null ) {
String contentType = this.oldRequest.getContentType( );
String queryString = this.oldRequest.getQueryString( );
String method = this.oldRequest.getMethod( );
Map workingParameters = new HashMap( );
try {
// Parse query string from request
if ( ( method.equals( METHOD_GET ) || method.equals( METHOD_HEAD ) ||
method.equals( METHOD_POST ) ) && ( queryString != null ) ) {
WinstoneRequest.extractParameters( queryString, this.encoding, workingParameters, false );
}
if ( method.equals( METHOD_POST ) && ( contentType != null )
&& ( contentType.equals( POST_PARAMETERS ) || contentType.startsWith( POST_PARAMETERS + ";" ) ) ) {
// Parse params
String paramLine = ( this.encoding == null ? new String( this.oldRequest.getBodyContent( ) )
: new String( this.oldRequest.getBodyContent( ), this.encoding ) );
WinstoneRequest.extractParameters( paramLine.trim( ), this.encoding, workingParameters, false );
}
this.parsedParams = workingParameters;
} catch ( UnsupportedEncodingException err ) {
Logger.log( Logger.ERROR, Launcher.RESOURCES, "WinstoneRequest.ErrorBodyParameters", err );
this.parsedParams = null;
}
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.w3c.dom.Node;
import winstone.Logger;
import winstone.Mapping;
import winstone.WebAppConfiguration;
/**
* Models a restriction on a particular set of resources in the webapp.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: SecurityConstraint.java, v 1.7 2006/08/10 06:38:30 rickknowles Exp $
*/
26 public class SecurityConstraint {
27 final String ELEM_DISPLAY_NAME = "display-name";
28 final String ELEM_WEB_RESOURCES = "web-resource-collection";
29 final String ELEM_WEB_RESOURCE_NAME = "web-resource-name";
30 final String ELEM_URL_PATTERN = "url-pattern";
31 final String ELEM_HTTP_METHOD = "http-method";
32 final String ELEM_AUTH_CONSTRAINT = "auth-constraint";
33 final String ELEM_ROLE_NAME = "role-name";
34 final String ELEM_USER_DATA_CONSTRAINT = "user-data-constraint";
35 final String ELEM_TRANSPORT_GUARANTEE = "transport-guarantee";
36 final String GUARANTEE_NONE = "NONE";
38 private String displayName;
39 private String methodSets[];
40 private Mapping urlPatterns[];
41 private String rolesAllowed[];
private boolean needsSSL;
/**
* Constructor
*/
47 public SecurityConstraint( Node elm, Set rolesAllowed, int counter ) {
this.needsSSL = false;
Set localUrlPatternList = new HashSet( );
Set localMethodSetList = new HashSet( );
Set localRolesAllowed = new HashSet( );
for ( int i = 0; i < elm.getChildNodes( ).getLength( ); i++ ) {
Node child = elm.getChildNodes( ).item( i );
if ( child.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( child.getNodeName( ).equals( ELEM_DISPLAY_NAME ) )
this.displayName = WebAppConfiguration.getTextFromNode( child );
else if ( child.getNodeName( ).equals( ELEM_WEB_RESOURCES ) ) {
String methodSet = null;
// Parse the element and extract
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node resourceChild = child.getChildNodes( ).item( k );
if ( resourceChild.getNodeType( ) != Node.ELEMENT_NODE )
continue;
String resourceChildNodeName = resourceChild.getNodeName( );
if ( resourceChildNodeName.equals( ELEM_URL_PATTERN ) ) {
localUrlPatternList.add( Mapping.createFromURL(
"Security", WebAppConfiguration.getTextFromNode( resourceChild ) ) );
} else if ( resourceChildNodeName.equals( ELEM_HTTP_METHOD ) ) {
methodSet = ( methodSet == null ? "." : methodSet )
+ WebAppConfiguration.getTextFromNode( resourceChild ) + ".";
}
}
localMethodSetList.add( methodSet == null ? ".ALL." : methodSet );
} else if ( child.getNodeName( ).equals( ELEM_AUTH_CONSTRAINT ) ) {
// Parse the element and extract
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node roleChild = child.getChildNodes( ).item( k );
if ( ( roleChild.getNodeType( ) != Node.ELEMENT_NODE )
|| !roleChild.getNodeName( ).equals( ELEM_ROLE_NAME ) )
continue;
String roleName = WebAppConfiguration.getTextFromNode( roleChild );
if ( roleName.equals( "*" ) )
localRolesAllowed.addAll( rolesAllowed );
else
localRolesAllowed.add( roleName );
}
} else if ( child.getNodeName( ).equals( ELEM_USER_DATA_CONSTRAINT ) ) {
// Parse the element and extract
for ( int k = 0; k < child.getChildNodes( ).getLength( ); k++ ) {
Node roleChild = child.getChildNodes( ).item( k );
if ( ( roleChild.getNodeType( ) == Node.ELEMENT_NODE )
&& roleChild.getNodeName( ).equals( ELEM_TRANSPORT_GUARANTEE ) )
this.needsSSL = !WebAppConfiguration.getTextFromNode( roleChild )
.equalsIgnoreCase( GUARANTEE_NONE );
}
}
}
this.urlPatterns = ( Mapping[] ) localUrlPatternList.toArray( new Mapping[0] );
this.methodSets = ( String[] ) localMethodSetList.toArray( new String[0] );
this.rolesAllowed = ( String[] ) localRolesAllowed.toArray( new String[0] );
if ( this.displayName == null )
this.displayName = BaseAuthenticationHandler.AUTH_RESOURCES.getString(
"SecurityConstraint.DefaultName", "" + counter );
}
/**
* Call this to evaluate the security constraint - is this operation allowed ?
*/
113 public boolean isAllowed( HttpServletRequest request ) {
for ( int n = 0; n < this.rolesAllowed.length; n++ ) {
if ( request.isUserInRole( this.rolesAllowed[n] ) ) {
Logger.log( Logger.FULL_DEBUG, BaseAuthenticationHandler.AUTH_RESOURCES,
"SecurityConstraint.Passed", new String[] {
this.displayName, this.rolesAllowed[n] } );
return true;
}
}
Logger.log( Logger.FULL_DEBUG, BaseAuthenticationHandler.AUTH_RESOURCES, "SecurityConstraint.Failed",
this.displayName );
return false;
}
/**
* Call this to evaluate the security constraint - is this constraint applicable to this url ?
*/
130 public boolean isApplicable( String url, String method ) {
for ( int n = 0; n < this.urlPatterns.length; n++ )
if ( this.urlPatterns[n].match( url, null, null )
&& methodCheck( method, this.methodSets[n] ) )
return true;
return false;
}
139 private boolean methodCheck( String protocol, String methodSet ) {
return methodSet.equals( ".ALL." )
|| ( methodSet.indexOf( "." + protocol.toUpperCase( ) + "." ) != -1 );
}
144 public boolean needsSSL( ) {
return this.needsSSL;
}
148 public String getName( ) {
return this.displayName;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.classLoader;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
/**
* This subclass of WinstoneClassLoader is the reloading version. It runs a
* monitoring thread in the background that checks for updates to any files in
* the class path.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ReloadingClassLoader.java, v 1.11 2007/02/17 01:55:12 rickknowles Exp $
*/
36 public class ReloadingClassLoader extends WebappClassLoader implements ServletContextListener, Runnable {
private static final int RELOAD_SEARCH_SLEEP = 10;
38 private static final WinstoneResourceBundle CL_RESOURCES = new WinstoneResourceBundle( "winstone.classLoader.LocalStrings" );
private boolean interrupted;
40 private WebAppConfiguration webAppConfig;
41 private Set loadedClasses;
42 private File classPaths[];
private int classPathsLength;
45 public ReloadingClassLoader( URL urls[], ClassLoader parent ) {
super( urls, parent );
this.loadedClasses = new HashSet( );
if ( urls != null ) {
this.classPaths = new File[urls.length];
for ( int n = 0 ; n < urls.length; n++ ) {
this.classPaths[this.classPathsLength++] = new File( urls[n].getFile( ) );
}
}
}
56 protected void addURL( URL url ) {
super.addURL( url );
synchronized ( this.loadedClasses ) {
if ( this.classPaths == null ) {
this.classPaths = new File[10];
this.classPathsLength = 0;
} else if ( this.classPathsLength == ( this.classPaths.length - 1 ) ) {
File temp[] = this.classPaths;
this.classPaths = new File[( int ) ( this.classPathsLength * 1.75 )];
System.arraycopy( temp, 0, this.classPaths, 0, this.classPathsLength );
}
this.classPaths[this.classPathsLength++] = new File( url.getFile( ) );
}
}
71 public void contextInitialized( ServletContextEvent sce ) {
this.webAppConfig = ( WebAppConfiguration ) sce.getServletContext( );
this.interrupted = false;
synchronized ( this ) {
this.loadedClasses.clear( );
}
Thread thread = new Thread( this, CL_RESOURCES
.getString( "ReloadingClassLoader.ThreadName" ) );
thread.setDaemon( true );
thread.setPriority( Thread.MIN_PRIORITY );
thread.start( );
}
84 public void contextDestroyed( ServletContextEvent sce ) {
this.interrupted = true;
this.webAppConfig = null;
synchronized ( this ) {
this.loadedClasses.clear( );
}
}
/**
* The maintenance thread. This makes sure that any changes in the files in
* the classpath trigger a classLoader self destruct and recreate.
*/
96 public void run( ) {
Logger.log( Logger.FULL_DEBUG, CL_RESOURCES,
"ReloadingClassLoader.MaintenanceThreadStarted" );
Map classDateTable = new HashMap( );
Map classLocationTable = new HashMap( );
Set lostClasses = new HashSet( );
while ( !interrupted ) {
try {
String loadedClassesCopy[] = null;
synchronized ( this ) {
loadedClassesCopy = ( String [] ) this.loadedClasses.toArray( new String[0] );
}
for ( int n = 0; ( n < loadedClassesCopy.length ) && !interrupted; n++ ) {
Thread.sleep( RELOAD_SEARCH_SLEEP );
String className = transformToFileFormat( loadedClassesCopy[n] );
File location = ( File ) classLocationTable.get( className );
Long classDate = null;
if ( ( location == null ) || !location.exists( ) ) {
for ( int j = 0; ( j < this.classPaths.length ) && ( classDate == null ); j++ ) {
File path = this.classPaths[j];
if ( !path.exists( ) ) {
continue;
} else if ( path.isDirectory( ) ) {
File classLocation = new File( path, className );
if ( classLocation.exists( ) ) {
classDate = new Long( classLocation.lastModified( ) );
classLocationTable.put( className, classLocation );
}
} else if ( path.isFile( ) ) {
classDate = searchJarPath( className, path );
if ( classDate != null )
classLocationTable.put( className, path );
}
}
} else if ( location.exists( ) )
classDate = new Long( location.lastModified( ) );
// Has class vanished ? Leave a note and skip over it
if ( classDate == null ) {
if ( !lostClasses.contains( className ) ) {
lostClasses.add( className );
Logger.log( Logger.DEBUG, CL_RESOURCES,
"ReloadingClassLoader.ClassLost", className );
}
continue;
}
if ( ( classDate != null ) && lostClasses.contains( className ) ) {
lostClasses.remove( className );
}
// Stash date of loaded files, and compare with last
// iteration
Long oldClassDate = ( Long ) classDateTable.get( className );
if ( oldClassDate == null ) {
classDateTable.put( className, classDate );
} else if ( oldClassDate.compareTo( classDate ) != 0 ) {
// Trigger reset of webAppConfig
Logger.log( Logger.INFO, CL_RESOURCES,
"ReloadingClassLoader.ReloadRequired",
new String[] {className,
"" + new Date( classDate.longValue( ) ),
"" + new Date( oldClassDate.longValue( ) ) } );
this.webAppConfig.resetClassLoader( );
}
}
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, CL_RESOURCES,
"ReloadingClassLoader.MaintenanceThreadError", err );
}
}
Logger.log( Logger.FULL_DEBUG, CL_RESOURCES,
"ReloadingClassLoader.MaintenanceThreadFinished" );
}
172 protected Class findClass( String name ) throws ClassNotFoundException {
synchronized ( this ) {
this.loadedClasses.add( "Class:" + name );
}
return super.findClass( name );
}
179 public URL findResource( String name ) {
synchronized ( this ) {
this.loadedClasses.add( name );
}
return super.findResource( name );
}
/**
* Iterates through a jar file searching for a class. If found, it returns that classes date
*/
189 private Long searchJarPath( String classResourceName, File path )
throws IOException, InterruptedException {
JarFile jar = new JarFile( path );
for ( Enumeration e = jar.entries( ); e.hasMoreElements( ) && !interrupted; ) {
JarEntry entry = ( JarEntry ) e.nextElement( );
if ( entry.getName( ).equals( classResourceName ) )
return new Long( path.lastModified( ) );
}
return null;
}
200 private static String transformToFileFormat( String name ) {
if ( !name.startsWith( "Class:" ) )
return name;
else
return WinstoneResourceBundle.globalReplace( name.substring( 6 ), ".", "/" ) + ".class";
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.classLoader;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import winstone.Logger;
import winstone.WinstoneResourceBundle;
/**
* Implements the servlet spec model ( v2.3 section 9.7.2 ) for classloading, which
* is different to the standard JDK model in that it delegates *after* checking
* local repositories. This has the effect of isolating copies of classes that exist
* in 2 webapps from each other.
*
* Thanks to James Berry for the changes to use the system classloader to prevent
* loading servlet spec or system classpath classes again.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WebappClassLoader.java, v 1.3 2007/12/29 03:32:54 rickknowles Exp $
*/
29 public class WebappClassLoader extends URLClassLoader {
30 private static final WinstoneResourceBundle CL_RESOURCES = new WinstoneResourceBundle( "winstone.classLoader.LocalStrings" );
32 protected ClassLoader system = getSystemClassLoader( );
34 public WebappClassLoader( URL[] urls ) {
super( urls );
}
38 public WebappClassLoader( URL[] urls, ClassLoader parent ) {
super( urls, parent );
}
42 public WebappClassLoader( URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory ) {
super( urls, parent, factory );
}
46 protected Class loadClass( String name, boolean resolve ) throws ClassNotFoundException {
// First, check if the class has already been loaded
Class c = findLoadedClass( name );
// Try the system loader first, to ensure that system classes are not
// overridden by webapps. Note that this includes any classes in winstone,
// including the javax.servlet classes
if ( c == null ) {
try {
c = system.loadClass( name );
if ( c != null ) {
Logger.log( Logger.MAX, CL_RESOURCES, "WebappClassLoader.LoadedBySystemCL", name );
}
} catch ( ClassNotFoundException e ) {
c = null;
}
}
// If an allowed class, load it locally first
if ( c == null ) {
try {
// If still not found, then invoke findClass in order to find the class.
c = findClass( name );
if ( c != null ) {
Logger.log( Logger.MAX, CL_RESOURCES, "WebappClassLoader.LoadedByThisCL", name );
}
} catch ( ClassNotFoundException e ) {
c = null;
}
}
// otherwise, and only if we have a parent, delegate to our parent
// Note that within winstone, the only difference between this and the system
// class loader we've already tried is that our parent might include the common/shared lib.
if ( c == null ) {
ClassLoader parent = getParent( );
if ( parent != null ) {
c = parent.loadClass( name );
if ( c != null ) {
Logger.log( Logger.MAX, CL_RESOURCES, "WebappClassLoader.LoadedByParentCL", name );
}
} else {
// We have no other hope for loading the class, so throw the class not found exception
throw new ClassNotFoundException( name );
}
}
if ( resolve && ( c != null ) ) {
resolveClass( c );
}
return c;
}
99 public InputStream getResourceAsStream( String name ) {
if ( ( name != null ) && name.startsWith( "/" ) ) {
name = name.substring( 1 );
}
return super.getResourceAsStream( name );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.cluster;
import java.net.*;
import java.io.*;
import winstone.Logger;
import winstone.WinstoneSession;
/**
* Contains all the logic for reading in sessions
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ClusterSessionSearch.java, v 1.6 2006/03/24 17:24:18 rickknowles Exp $
*/
21 public class ClusterSessionSearch implements Runnable {
final int TIMEOUT = 2000;
public static final byte SESSION_CHECK_TYPE = ( byte ) '1';
24 public static final String SESSION_NOT_FOUND = "NOTFOUND";
25 public static final String SESSION_FOUND = "FOUND";
26 public static final String SESSION_RECEIVED = "OK";
private boolean isFinished;
// private boolean interrupted;
29 private WinstoneSession result;
30 private String searchWebAppHostname;
31 private String searchWebAppPrefix;
32 private String searchId;
33 private String searchAddressPort;
private int controlPort;
/**
* Sets up for a threaded search
*/
39 public ClusterSessionSearch( String webAppPrefix, String hostName, String sessionId,
40 String ipPort, int controlPort ) {
this.isFinished = false;
this.searchWebAppHostname = hostName;
this.searchWebAppPrefix = webAppPrefix;
// this.interrupted = false;
this.searchId = sessionId;
this.searchAddressPort = ipPort;
this.result = null;
this.controlPort = controlPort;
// Start the search thread
Thread searchThread = new Thread( this );
searchThread.setDaemon( true );
searchThread.start( );
}
/**
* Actually implements the search
*/
59 public void run( ) {
try {
int colonPos = this.searchAddressPort.indexOf( ':' );
String ipAddress = this.searchAddressPort.substring( 0, colonPos );
String port = this.searchAddressPort.substring( colonPos + 1 );
Socket controlConnection = new Socket( ipAddress, Integer.parseInt( port ) );
controlConnection.setSoTimeout( TIMEOUT );
OutputStream out = controlConnection.getOutputStream( );
out.write( SESSION_CHECK_TYPE );
out.flush( );
ObjectOutputStream outControl = new ObjectOutputStream( out );
outControl.writeInt( this.controlPort );
outControl.writeUTF( this.searchId );
outControl.writeUTF( this.searchWebAppHostname );
outControl.writeUTF( this.searchWebAppPrefix );
outControl.flush( );
InputStream in = controlConnection.getInputStream( );
ObjectInputStream inSession = new ObjectInputStream( in );
String reply = inSession.readUTF( );
if ( ( reply != null ) && reply.equals( SESSION_FOUND ) ) {
WinstoneSession session = ( WinstoneSession ) inSession
.readObject( );
outControl.writeUTF( SESSION_RECEIVED );
this.result = session;
}
outControl.close( );
inSession.close( );
out.close( );
in.close( );
controlConnection.close( );
} catch ( Throwable err ) {
Logger.log( Logger.WARNING, SimpleCluster.CLUSTER_RESOURCES,
"ClusterSessionSearch.Error", err );
}
this.isFinished = true;
}
98 public boolean isFinished( ) {
return this.isFinished;
}
102 public WinstoneSession getResult( ) {
return this.result;
}
106 public void destroy( ) {
// this.interrupted = true;
}
110 public String getAddressPort( ) {
return this.searchAddressPort;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.cluster;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import winstone.Cluster;
import winstone.HostConfiguration;
import winstone.HostGroup;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
import winstone.WinstoneSession;
/**
* Represents a cluster of winstone containers.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: SimpleCluster.java, v 1.8 2006/08/10 06:38:31 rickknowles Exp $
*/
41 public class SimpleCluster implements Runnable, Cluster {
final int SESSION_CHECK_TIMEOUT = 100;
final int HEARTBEAT_PERIOD = 5000;
final int MAX_NO_OF_MISSING_HEARTBEATS = 3;
final byte NODELIST_DOWNLOAD_TYPE = ( byte ) '2';
final byte NODE_HEARTBEAT_TYPE = ( byte ) '3';
48 public static final WinstoneResourceBundle CLUSTER_RESOURCES = new WinstoneResourceBundle( "winstone.cluster.LocalStrings" );
private int controlPort;
50 private String initialClusterNodes;
51 private Map clusterAddresses;
private boolean interrupted;
/**
* Builds a cluster instance
*/
57 public SimpleCluster( Map args, Integer controlPort ) {
this.interrupted = false;
this.clusterAddresses = new Hashtable( );
if ( controlPort != null )
this.controlPort = controlPort.intValue( );
// Start cluster init thread
this.initialClusterNodes = ( String ) args.get( "clusterNodes" );
Thread thread = new Thread( this, CLUSTER_RESOURCES
.getString( "SimpleCluster.ThreadName" ) );
thread.setDaemon( true );
thread.setPriority( Thread.MIN_PRIORITY );
thread.start( );
}
72 public void destroy( ) {
this.interrupted = true;
}
/**
* Send a heartbeat every now and then, and remove any nodes that haven't
* responded in 3 heartbeats.
*/
80 public void run( ) {
// Ask each of the known addresses for their cluster lists, and build a
// set
if ( this.initialClusterNodes != null ) {
StringTokenizer st = new StringTokenizer( this.initialClusterNodes,
", " );
while ( st.hasMoreTokens( ) && !interrupted )
askClusterNodeForNodeList( st.nextToken( ) );
}
Logger.log( Logger.DEBUG, CLUSTER_RESOURCES, "SimpleCluster.InitNodes", ""
+ this.clusterAddresses.size( ) );
while ( !interrupted ) {
try {
Set addresses = new HashSet( this.clusterAddresses.keySet( ) );
Date noHeartbeatDate = new Date( System.currentTimeMillis( )
- ( MAX_NO_OF_MISSING_HEARTBEATS * HEARTBEAT_PERIOD ) );
for ( Iterator i = addresses.iterator( ); i.hasNext( ); ) {
String ipPort = ( String ) i.next( );
Date lastHeartBeat = ( Date ) this.clusterAddresses
.get( ipPort );
if ( lastHeartBeat.before( noHeartbeatDate ) ) {
this.clusterAddresses.remove( ipPort );
Logger.log( Logger.FULL_DEBUG, CLUSTER_RESOURCES,
"SimpleCluster.RemovingNode", ipPort );
}
// Send heartbeat
else
sendHeartbeat( ipPort );
}
Thread.sleep( HEARTBEAT_PERIOD );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, CLUSTER_RESOURCES,
"SimpleCluster.ErrorMonitorThread", err );
}
}
Logger.log( Logger.FULL_DEBUG, CLUSTER_RESOURCES,
"SimpleCluster.FinishedMonitorThread" );
}
/**
* Check if the other nodes in this cluster have a session for this
* sessionId.
*
* @param sessionId The id of the session to check for
* @return A valid session instance
*/
131 public WinstoneSession askClusterForSession( String sessionId,
132 WebAppConfiguration webAppConfig ) {
// Iterate through the cluster members
Collection addresses = new ArrayList( clusterAddresses.keySet( ) );
Collection searchThreads = new ArrayList( );
for ( Iterator i = addresses.iterator( ); i.hasNext( ); ) {
String ipPort = ( String ) i.next( );
ClusterSessionSearch search = new ClusterSessionSearch(
webAppConfig.getContextPath( ), webAppConfig.getOwnerHostname( ),
sessionId, ipPort, this.controlPort );
searchThreads.add( search );
}
// Wait until we get an answer
WinstoneSession answer = null;
String senderThread = null;
boolean finished = false;
while ( !finished ) {
// Loop through all search threads. If finished, exit, otherwise
// sleep
List finishedThreads = new ArrayList( );
for ( Iterator i = searchThreads.iterator( ); i.hasNext( ); ) {
ClusterSessionSearch searchThread = ( ClusterSessionSearch ) i
.next( );
if ( !searchThread.isFinished( ) )
continue;
else if ( searchThread.getResult( ) == null )
finishedThreads.add( searchThread );
else {
answer = searchThread.getResult( );
senderThread = searchThread.getAddressPort( );
}
}
// Remove finished threads
for ( Iterator i = finishedThreads.iterator( ); i.hasNext( ); )
searchThreads.remove( i.next( ) );
if ( searchThreads.isEmpty( ) || ( answer != null ) )
finished = true;
else
try {
Thread.sleep( 100 );
} catch ( InterruptedException err ) {
}
}
// Once we have an answer, terminate all search threads
for ( Iterator i = searchThreads.iterator( ); i.hasNext( ); ) {
ClusterSessionSearch searchThread = ( ClusterSessionSearch ) i.next( );
searchThread.destroy( );
}
if ( answer != null ) {
answer.activate( webAppConfig );
Logger.log( Logger.DEBUG, CLUSTER_RESOURCES,
"SimpleCluster.SessionTransferredFrom", senderThread );
}
return answer;
}
/**
* Given an address, retrieve the list of cluster nodes and initialise dates
*
* @param address The address to request a node list from
*/
196 private void askClusterNodeForNodeList( String address ) {
try {
int colonPos = address.indexOf( ':' );
String ipAddress = address.substring( 0, colonPos );
String port = address.substring( colonPos + 1 );
Socket clusterListSocket = new Socket( ipAddress,
Integer.parseInt( port ) );
this.clusterAddresses.put( clusterListSocket.getInetAddress( )
.getHostAddress( ) + ":" + port, new Date( ) );
InputStream in = clusterListSocket.getInputStream( );
OutputStream out = clusterListSocket.getOutputStream( );
out.write( NODELIST_DOWNLOAD_TYPE );
out.flush( );
// Write out the control port
ObjectOutputStream outControl = new ObjectOutputStream( out );
outControl.writeInt( this.controlPort );
outControl.flush( );
// For each node, add an entry to cluster nodes
ObjectInputStream inData = new ObjectInputStream( in );
int nodeCount = inData.readInt( );
for ( int n = 0; n < nodeCount; n++ )
this.clusterAddresses.put( inData.readUTF( ), new Date( ) );
inData.close( );
outControl.close( );
out.close( );
in.close( );
clusterListSocket.close( );
} catch ( ConnectException err ) {
Logger.log( Logger.DEBUG, CLUSTER_RESOURCES,
"SimpleCluster.NoNodeListResponse", address );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, CLUSTER_RESOURCES,
"SimpleCluster.ErrorGetNodeList", address, err );
}
}
/**
* Given an address, send a heartbeat
*
* @param address The address to request a node list from
*/
240 private void sendHeartbeat( String address ) {
try {
int colonPos = address.indexOf( ':' );
String ipAddress = address.substring( 0, colonPos );
String port = address.substring( colonPos + 1 );
Socket heartbeatSocket = new Socket( ipAddress,
Integer.parseInt( port ) );
OutputStream out = heartbeatSocket.getOutputStream( );
out.write( NODE_HEARTBEAT_TYPE );
out.flush( );
ObjectOutputStream outData = new ObjectOutputStream( out );
outData.writeInt( this.controlPort );
outData.close( );
heartbeatSocket.close( );
Logger.log( Logger.FULL_DEBUG, CLUSTER_RESOURCES,
"SimpleCluster.HeartbeatSent", address );
} catch ( ConnectException err ) {/* ignore - 3 fails, and we remove */
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, CLUSTER_RESOURCES,
"SimpleCluster.HeartbeatError", address, err );
}
}
/**
* Accept a control socket request related to the cluster functions and
* process the request.
*
* @param requestType A byte indicating the request type
* @param in Socket input stream
* @param outSocket output stream
* @param webAppConfig Instance of the web app
* @throws IOException
*/
273 public void clusterRequest( byte requestType, InputStream in,
274 OutputStream out, Socket socket, HostGroup hostGroup )
throws IOException {
if ( requestType == ClusterSessionSearch.SESSION_CHECK_TYPE )
handleClusterSessionRequest( socket, in, out, hostGroup );
else if ( requestType == NODELIST_DOWNLOAD_TYPE )
handleNodeListDownloadRequest( socket, in, out );
else if ( requestType == NODE_HEARTBEAT_TYPE )
handleNodeHeartBeatRequest( socket, in );
else
Logger.log( Logger.ERROR, CLUSTER_RESOURCES,
"SimpleCluster.UnknownRequest", "" + ( char ) requestType );
}
/**
* Handles incoming socket requests for session search
*/
290 public void handleClusterSessionRequest( Socket socket, InputStream in,
291 OutputStream out, HostGroup hostGroup )
throws IOException {
// Read in a string for the sessionId
ObjectInputStream inControl = new ObjectInputStream( in );
int port = inControl.readInt( );
String ipPortSender = socket.getInetAddress( ).getHostAddress( ) + ":" + port;
String sessionId = inControl.readUTF( );
String hostname = inControl.readUTF( );
HostConfiguration hostConfig = hostGroup.getHostByName( hostname );
String webAppPrefix = inControl.readUTF( );
WebAppConfiguration webAppConfig = hostConfig.getWebAppByURI( webAppPrefix );
ObjectOutputStream outData = new ObjectOutputStream( out );
if ( webAppConfig == null ) {
outData.writeUTF( ClusterSessionSearch.SESSION_NOT_FOUND );
} else {
WinstoneSession session = webAppConfig.getSessionById( sessionId, true );
if ( session != null ) {
outData.writeUTF( ClusterSessionSearch.SESSION_FOUND );
outData.writeObject( session );
outData.flush( );
if ( inControl.readUTF( ).equals(
ClusterSessionSearch.SESSION_RECEIVED ) )
session.passivate( );
Logger.log( Logger.DEBUG, CLUSTER_RESOURCES,
"SimpleCluster.SessionTransferredTo", ipPortSender );
} else {
outData.writeUTF( ClusterSessionSearch.SESSION_NOT_FOUND );
}
}
outData.close( );
inControl.close( );
}
/**
* Handles incoming socket requests for cluster node lists.
*/
327 public void handleNodeListDownloadRequest( Socket socket, InputStream in,
328 OutputStream out ) throws IOException {
// Get the ip and port of the requester, and make sure we don't send
// that
ObjectInputStream inControl = new ObjectInputStream( in );
int port = inControl.readInt( );
String ipPortSender = socket.getInetAddress( ).getHostAddress( ) + ":"
+ port;
List allClusterNodes = new ArrayList( this.clusterAddresses.keySet( ) );
List relevantClusterNodes = new ArrayList( );
for ( Iterator i = allClusterNodes.iterator( ); i.hasNext( ); ) {
String node = ( String ) i.next( );
if ( !node.equals( ipPortSender ) )
relevantClusterNodes.add( node );
}
ObjectOutputStream outData = new ObjectOutputStream( out );
outData.writeInt( relevantClusterNodes.size( ) );
outData.flush( );
for ( Iterator i = relevantClusterNodes.iterator( ); i.hasNext( ); ) {
String ipPort = ( String ) i.next( );
if ( !ipPort.equals( ipPortSender ) )
outData.writeUTF( ipPort );
outData.flush( );
}
outData.close( );
inControl.close( );
}
/**
* Handles heartbeats. Just updates the date of this node's last heartbeat
*/
359 public void handleNodeHeartBeatRequest( Socket socket, InputStream in )
throws IOException {
ObjectInputStream inData = new ObjectInputStream( in );
int remoteControlPort = inData.readInt( );
inData.close( );
String ipPort = socket.getInetAddress( ).getHostAddress( ) + ":"
+ remoteControlPort;
this.clusterAddresses.put( ipPort, new Date( ) );
Logger.log( Logger.FULL_DEBUG, CLUSTER_RESOURCES,
"SimpleCluster.HeartbeatReceived", ipPort );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.invoker;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import winstone.Logger;
import winstone.Mapping;
import winstone.RequestDispatcher;
import winstone.ServletConfiguration;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
/**
* If a URI matches a servlet class name, mount an instance of that servlet, and
* try to process the request using that servlet.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: InvokerServlet.java, v 1.6 2006/03/24 17:24:24 rickknowles Exp $
*/
34 public class InvokerServlet extends HttpServlet {
// private static final String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
36 private static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
38 private static final WinstoneResourceBundle INVOKER_RESOURCES =
new WinstoneResourceBundle( "winstone.invoker.LocalStrings" );
40 private Map mountedInstances;
// private String prefix;
// private String invokerPrefix;
/**
* Set up a blank map of servlet configuration instances
*/
47 public void init( ServletConfig config ) throws ServletException {
super.init( config );
this.mountedInstances = new Hashtable( );
// this.prefix = config.getInitParameter( "prefix" );
// this.invokerPrefix = config.getInitParameter( "invokerPrefix" );
}
/**
* Destroy any mounted instances we might be holding, then destroy myself
*/
57 public void destroy( ) {
if ( this.mountedInstances != null ) {
synchronized ( this.mountedInstances ) {
for ( Iterator i = this.mountedInstances.values( ).iterator( ); i
.hasNext( ); )
( ( ServletConfiguration ) i.next( ) ).destroy( );
this.mountedInstances.clear( );
}
}
this.mountedInstances = null;
// this.prefix = null;
// this.invokerPrefix = null;
}
/**
* Get an instance of the servlet configuration object
*/
74 protected ServletConfiguration getInvokableInstance( String servletName )
throws ServletException, IOException {
ServletConfiguration sc = null;
synchronized ( this.mountedInstances ) {
if ( this.mountedInstances.containsKey( servletName ) ) {
sc = ( ServletConfiguration ) this.mountedInstances.get( servletName );
}
}
if ( sc == null ) {
// If found, mount an instance
try {
// Class servletClass = Class.forName( servletName, true,
// Thread.currentThread( ).getContextClassLoader( ) );
sc = new ServletConfiguration( ( WebAppConfiguration ) this.getServletContext( ),
getServletConfig( ).getServletName( ) + ":" + servletName, servletName,
new Hashtable( ), -1 );
this.mountedInstances.put( servletName, sc );
Logger.log( Logger.DEBUG, INVOKER_RESOURCES,
"InvokerServlet.MountingServlet", new String[] {
servletName,
getServletConfig( ).getServletName( ) } );
// just to trigger the servlet.init( )
sc.ensureInitialization( );
} catch ( Throwable err ) {
sc = null;
}
}
return sc;
}
105 protected void doGet( HttpServletRequest req, HttpServletResponse rsp )
throws ServletException, IOException {
boolean isInclude = ( req.getAttribute( INCLUDE_PATH_INFO ) != null );
// boolean isForward = ( req.getAttribute( FORWARD_PATH_INFO ) != null );
String servletName = null;
if ( isInclude )
servletName = ( String ) req.getAttribute( INCLUDE_PATH_INFO );
// else if ( isForward )
// servletName = ( String ) req.getAttribute( FORWARD_PATH_INFO );
else if ( req.getPathInfo( ) != null )
servletName = req.getPathInfo( );
else
servletName = "";
if ( servletName.startsWith( "/" ) )
servletName = servletName.substring( 1 );
ServletConfiguration invokedServlet = getInvokableInstance( servletName );
if ( invokedServlet == null ) {
Logger.log( Logger.WARNING, INVOKER_RESOURCES,
"InvokerServlet.NoMatchingServletFound", servletName );
rsp.sendError( HttpServletResponse.SC_NOT_FOUND, INVOKER_RESOURCES
.getString( "InvokerServlet.NoMatchingServletFound",
servletName ) );
} else {
RequestDispatcher rd = new RequestDispatcher(
( WebAppConfiguration ) getServletContext( ),
invokedServlet );
rd.setForNamedDispatcher( new Mapping[0], new Mapping[0] );
rd.forward( req, rsp );
}
}
138 protected void doPost( HttpServletRequest req, HttpServletResponse rsp )
throws ServletException, IOException {
doGet( req, rsp );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import winstone.JNDIManager;
import winstone.Logger;
import winstone.WinstoneResourceBundle;
import winstone.jndi.resourceFactories.WinstoneDataSource;
/**
* Implements a simple web.xml + command line arguments style jndi manager
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ContainerJNDIManager.java, v 1.3 2006/02/28 07:32:48 rickknowles Exp $
*/
36 public class ContainerJNDIManager implements JNDIManager {
37 public static final WinstoneResourceBundle JNDI_RESOURCES = new WinstoneResourceBundle( "winstone.jndi.LocalStrings" );
39 protected Map objectsToCreate;
/**
* Gets the relevant list of objects from the args, validating against the
* web.xml nodes supplied. All node addresses are assumed to be relative to
* the java:/comp/env context
*/
46 public ContainerJNDIManager( Map args, List webXmlNodes, ClassLoader loader ) {
// Build all the objects we wanted
this.objectsToCreate = new HashMap( );
Collection keys = new ArrayList( args != null ? args.keySet( ) : ( Collection ) new ArrayList( ) );
for ( Iterator i = keys.iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
if ( key.startsWith( "jndi.resource." ) ) {
String resName = key.substring( 14 );
String className = ( String ) args.get( key );
String value = ( String ) args.get( "jndi.param." + resName
+ ".value" );
Logger.log( Logger.FULL_DEBUG, JNDI_RESOURCES,
"ContainerJNDIManager.CreatingResourceArgs", resName );
Object obj = createObject( resName.trim( ), className.trim( ),
value, args, loader );
if ( obj != null )
this.objectsToCreate.put( resName, obj );
}
}
}
/**
* Add the objects passed to the constructor to the JNDI Context addresses
* specified
*/
73 public void setup( ) {
try {
InitialContext ic = new InitialContext( );
for ( Iterator i = this.objectsToCreate.keySet( ).iterator( ); i.hasNext( ); ) {
String name = ( String ) i.next( );
try {
Name fullName = new CompositeName( name );
Context currentContext = ic;
while ( fullName.size( ) > 1 ) {
// Make contexts that are not already present
try {
currentContext = currentContext
.createSubcontext( fullName.get( 0 ) );
} catch ( NamingException err ) {
currentContext = ( Context ) currentContext
.lookup( fullName.get( 0 ) );
}
fullName = fullName.getSuffix( 1 );
}
ic.bind( name, this.objectsToCreate.get( name ) );
Logger.log( Logger.FULL_DEBUG, JNDI_RESOURCES,
"ContainerJNDIManager.BoundResource", name );
} catch ( NamingException err ) {
Logger.log( Logger.ERROR, JNDI_RESOURCES,
"ContainerJNDIManager.ErrorBindingResource",
name, err );
}
}
Logger.log( Logger.DEBUG, JNDI_RESOURCES,
"ContainerJNDIManager.SetupComplete", "" + this.objectsToCreate.size( ) );
} catch ( NamingException err ) {
Logger.log( Logger.ERROR, JNDI_RESOURCES,
"ContainerJNDIManager.ErrorGettingInitialContext", err );
}
}
/**
* Remove the objects under administration from the JNDI Context, and then
* destroy the objects
*/
114 public void tearDown( ) {
try {
InitialContext ic = new InitialContext( );
for ( Iterator i = this.objectsToCreate.keySet( ).iterator( ); i
.hasNext( ); ) {
String name = ( String ) i.next( );
try {
ic.unbind( name );
} catch ( NamingException err ) {
Logger.log( Logger.ERROR, JNDI_RESOURCES,
"ContainerJNDIManager.ErrorUnbindingResource", name,
err );
}
Object unboundObject = this.objectsToCreate.get( name );
if ( unboundObject instanceof WinstoneDataSource )
( ( WinstoneDataSource ) unboundObject ).destroy( );
Logger.log( Logger.FULL_DEBUG, JNDI_RESOURCES,
"ContainerJNDIManager.UnboundResource", name );
}
Logger.log( Logger.DEBUG, JNDI_RESOURCES,
"ContainerJNDIManager.TeardownComplete", "" + this.objectsToCreate.size( ) );
} catch ( NamingException err ) {
Logger.log( Logger.ERROR, JNDI_RESOURCES,
"ContainerJNDIManager.ErrorGettingInitialContext", err );
}
}
/**
* Build an object to insert into the jndi space
*/
144 protected Object createObject( String name, String className, String value,
145 Map args, ClassLoader loader ) {
if ( ( className == null ) || ( name == null ) )
return null;
// Set context class loader
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
Thread.currentThread( ).setContextClassLoader( loader );
try {
// If we are working with a datasource
if ( className.equals( "javax.sql.DataSource" ) ) {
try {
return new WinstoneDataSource( name, extractRelevantArgs( args, name ), loader );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, JNDI_RESOURCES,
"ContainerJNDIManager.ErrorBuildingDatasource", name, err );
}
}
// If we are working with a mail session
else if ( className.equals( "javax.mail.Session" ) ) {
try {
Class smtpClass = Class.forName( className, true, loader );
Method smtpMethod = smtpClass.getMethod( "getInstance",
new Class[] { Properties.class,
Class.forName( "javax.mail.Authenticator" ) } );
return smtpMethod.invoke( null, new Object[] {
extractRelevantArgs( args, name ), null } );
//return Session.getInstance( extractRelevantArgs( args, name ), null );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, JNDI_RESOURCES,
"ContainerJNDIManager.ErrorBuildingMailSession",
name, err );
}
}
// If unknown type, try to instantiate with the string constructor
else if ( value != null ) {
try {
Class objClass = Class.forName( className.trim( ), true, loader );
Constructor objConstr = objClass
.getConstructor( new Class[] { String.class } );
return objConstr.newInstance( new Object[] { value } );
} catch ( Throwable err ) {
Logger.log( Logger.ERROR, JNDI_RESOURCES,
"ContainerJNDIManager.ErrorBuildingObject", new String[] {
name, className }, err );
}
}
return null;
} finally {
Thread.currentThread( ).setContextClassLoader( cl );
}
}
/**
* Rips the parameters relevant to a particular resource from the command args
*/
206 private Properties extractRelevantArgs( Map input, String name ) {
Properties relevantArgs = new Properties( );
for ( Iterator i = input.keySet( ).iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
if ( key.startsWith( "jndi.param." + name + "." ) )
relevantArgs.put( key.substring( 12 + name.length( ) ), input
.get( key ) );
}
return relevantArgs;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Node;
import winstone.Logger;
import winstone.WebAppConfiguration;
/**
* Implements a simple web.xml + command line arguments style jndi manager
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WebAppJNDIManager.java, v 1.9 2006/02/28 07:32:48 rickknowles Exp $
*/
24 public class WebAppJNDIManager extends ContainerJNDIManager {
25 final static String ELEM_ENV_ENTRY = "env-entry";
26 final static String ELEM_ENV_ENTRY_NAME = "env-entry-name";
27 final static String ELEM_ENV_ENTRY_TYPE = "env-entry-type";
28 final static String ELEM_ENV_ENTRY_VALUE = "env-entry-value";
/**
* Gets the relevant list of objects from the args, validating against the
* web.xml nodes supplied. All node addresses are assumed to be relative to
* the java:/comp/env context
*/
35 public WebAppJNDIManager( Map args, List webXMLNodes, ClassLoader loader ) {
super( args, webXMLNodes, loader );
// If the webXML nodes are not null, validate that all the entries we
// wanted have been created
if ( webXMLNodes != null )
for ( Iterator i = webXMLNodes.iterator( ); i.hasNext( ); ) {
Node node = ( Node ) i.next( );
// Extract the env-entry nodes and create the objects
if ( node.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( node.getNodeName( ).equals( ELEM_ENV_ENTRY ) ) {
String name = null;
String type = null;
String value = null;
for ( int m = 0; m < node.getChildNodes( ).getLength( ); m++ ) {
Node envNode = node.getChildNodes( ).item( m );
if ( envNode.getNodeType( ) != Node.ELEMENT_NODE )
continue;
else if ( envNode.getNodeName( ).equals( ELEM_ENV_ENTRY_NAME ) )
name = WebAppConfiguration.getTextFromNode( envNode );
else if ( envNode.getNodeName( ).equals( ELEM_ENV_ENTRY_TYPE ) )
type = WebAppConfiguration.getTextFromNode( envNode );
else if ( envNode.getNodeName( ).equals( ELEM_ENV_ENTRY_VALUE ) )
value = WebAppConfiguration.getTextFromNode( envNode );
}
if ( ( name != null ) && ( type != null ) && ( value != null ) ) {
Logger.log( Logger.FULL_DEBUG, JNDI_RESOURCES,
"WebAppJNDIManager.CreatingResourceWebXML",
name );
Object obj = createObject( name, type, value, args, loader );
if ( obj != null )
this.objectsToCreate.put( name, obj );
}
}
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.naming.Binding;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.spi.NamingManager;
/**
* Enumeration over the set of bindings for this context.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneBindingEnumeration.java, v 1.3 2006/02/28 07:32:48 rickknowles Exp $
*/
27 public class WinstoneBindingEnumeration implements NamingEnumeration {
28 private Enumeration nameEnumeration;
29 private Hashtable bindings;
30 private Hashtable contextEnvironment;
31 private Context context;
/**
* Constructor - sets up the enumeration ready for retrieving bindings
* instead of NameClassPairs.
*
* @param bindings
* The source binding set
*/
40 public WinstoneBindingEnumeration( Hashtable bindings,
41 Hashtable environment, Context context ) {
Object keys[] = bindings.keySet( ).toArray( );
Arrays.sort( keys );
Vector nameList = new Vector( Arrays.asList( keys ) );
this.nameEnumeration = nameList.elements( );
this.bindings = ( Hashtable ) bindings.clone( );
this.context = context;
this.contextEnvironment = environment;
}
51 public Object next( ) throws NamingException {
if ( this.nameEnumeration == null )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES
.getString( "WinstoneBindingEnumeration.AlreadyClosed" ) );
String name = ( String ) this.nameEnumeration.nextElement( );
Object value = this.bindings.get( name );
try {
value = NamingManager.getObjectInstance( value, new CompositeName( )
.add( name ), this.context, this.contextEnvironment );
} catch ( Throwable err ) {
NamingException errNaming = new NamingException(
ContainerJNDIManager.JNDI_RESOURCES
.getString( "WinstoneBindingEnumeration.FailedToGetInstance" ) );
errNaming.setRootCause( err );
throw errNaming;
}
return new Binding( name, value );
}
71 public boolean hasMore( ) throws NamingException {
if ( this.nameEnumeration == null )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES
.getString( "WinstoneBindingEnumeration.AlreadyClosed" ) );
else
return this.nameEnumeration.hasMoreElements( );
}
79 public void close( ) throws NamingException {
this.nameEnumeration = null;
}
83 public boolean hasMoreElements( ) {
try {
return hasMore( );
} catch ( NamingException err ) {
return false;
}
}
91 public Object nextElement( ) {
try {
return next( );
} catch ( NamingException err ) {
return null;
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameNotFoundException;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.NotContextException;
import javax.naming.OperationNotSupportedException;
import javax.naming.spi.NamingManager;
import winstone.Logger;
/**
* The main jndi context implementation class.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneContext.java, v 1.3 2006/02/28 07:32:48 rickknowles Exp $
*/
34 public class WinstoneContext implements Context {
35 static final String PREFIX = "java:";
36 static final String FIRST_CHILD = "comp";
37 static final String BODGED_PREFIX = "java:comp";
39 private Hashtable environment;
40 private Hashtable bindings;
41 private final static NameParser nameParser = new WinstoneNameParser( );
private WinstoneContext parent;
43 private String myAbsoluteName;
44 private Object contextLock;
/**
* Constructor - sets up environment
*/
49 public WinstoneContext( Map sourceEnvironment, WinstoneContext parent,
50 String absoluteName, Object contextLock ) throws NamingException {
this.environment = new Hashtable( );
List sourceKeys = new ArrayList( sourceEnvironment.keySet( ) );
for ( Iterator i = sourceKeys.iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
addToEnvironment( key, sourceEnvironment.get( key ) );
}
this.parent = parent;
this.myAbsoluteName = absoluteName;
this.contextLock = contextLock;
this.bindings = new Hashtable( );
Logger.log( Logger.FULL_DEBUG, ContainerJNDIManager.JNDI_RESOURCES,
"WinstoneContext.Initialised", this.myAbsoluteName );
}
/**
* Constructor - sets up environment and copies the bindings across
*/
68 protected WinstoneContext( Map sourceEnvironment, WinstoneContext parent,
69 String absoluteName, Object contextLock, Hashtable bindings ) throws NamingException {
this.environment = new Hashtable( );
List sourceKeys = new ArrayList( sourceEnvironment.keySet( ) );
for ( Iterator i = sourceKeys.iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
addToEnvironment( key, sourceEnvironment.get( key ) );
}
this.parent = parent;
this.myAbsoluteName = absoluteName;
this.contextLock = contextLock;
this.bindings = bindings;
Logger.log( Logger.FULL_DEBUG, ContainerJNDIManager.JNDI_RESOURCES,
"WinstoneContext.Copied", this.myAbsoluteName );
}
84 public void close( ) throws NamingException {
}
87 public Hashtable getEnvironment( ) throws NamingException {
return new Hashtable( this.environment );
}
91 public Object removeFromEnvironment( String property ) throws NamingException {
return this.environment.remove( property );
}
95 public Object addToEnvironment( String property, Object value )
throws NamingException {
return this.environment.put( property, value );
}
/**
* Handles the processing of relative and absolute names. If a relative name
* is detected, it is processed by the name parser. If an absolute name is
* detected, it determines first if the absolute name refers to this
* context. If not, it then determines whether the request can be passed
* back to the parent or not, and returns null if it can, and throws an
* exception otherwise.
*/
108 protected Name validateName( Name name ) throws NamingException {
// Check for absolute urls and redirect or correct
if ( name.isEmpty( ) )
return name;
else if ( name.get( 0 ).equals( BODGED_PREFIX ) ) {
Name newName = name.getSuffix( 1 ).add( 0, FIRST_CHILD ).add( 0, PREFIX );
return validateName( newName );
} else if ( name.get( 0 ).equals( PREFIX ) ) {
String stringName = name.toString( );
if ( stringName.equals( this.myAbsoluteName ) )
return nameParser.parse( "" );
else if ( stringName.startsWith( this.myAbsoluteName ) )
return nameParser.parse( stringName
.substring( this.myAbsoluteName.length( ) + 1 ) );
else if ( this.parent != null )
return null;
else
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", name.toString( ) ) );
} else if ( name instanceof CompositeName )
return nameParser.parse( name.toString( ) );
else
return name;
}
/**
* Lookup an object in the context. Returns a copy of this context if the
* name is empty, or the specified resource ( if we have it ). If the name is
* unknown, throws a NameNotFoundException.
*/
138 public Object lookup( Name name ) throws NamingException {
Name searchName = validateName( name );
// If null, it means we don't know how to handle this -> throw to the
// parent
if ( searchName == null )
return this.parent.lookup( name );
// If empty name, return a copy of this Context
else if ( searchName.isEmpty( ) )
return new WinstoneContext( this.environment, this.parent,
this.myAbsoluteName, this.contextLock, this.bindings );
String thisName = searchName.get( 0 );
synchronized ( this.contextLock ) {
Object thisValue = bindings.get( thisName );
// If the name points to something in this level, try to find it,
// and give
// an error if not available
if ( searchName.size( ) == 1 ) {
if ( thisValue == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", name.toString( ) ) );
try {
return NamingManager.getObjectInstance( thisValue,
new CompositeName( ).add( thisName ), this,
this.environment );
} catch ( Exception e ) {
NamingException ne = new NamingException( ContainerJNDIManager.JNDI_RESOURCES
.getString( "WinstoneContext.FailedToGetInstance" ) );
ne.setRootCause( e );
throw ne;
}
}
else if ( thisValue == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", thisName.toString( ) ) );
// If it's not in this level and what we found is not a context,
// complain
else if ( !( thisValue instanceof Context ) )
throw new NotContextException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
thisName.toString( ),
thisValue.getClass( ).getName( ) } ) );
// Open the context, perform a lookup, then close the context we
// opened
else
try {
return ( ( Context ) thisValue )
.lookup( searchName.getSuffix( 1 ) );
} finally {
( ( Context ) thisValue ).close( );
}
}
}
198 public Object lookup( String name ) throws NamingException {
return lookup( new CompositeName( name ) );
}
202 public Object lookupLink( Name name ) throws NamingException {
Logger.log( Logger.WARNING, ContainerJNDIManager.JNDI_RESOURCES,
"WinstoneContext.LinkRefUnsupported" );
return lookup( name );
}
208 public Object lookupLink( String name ) throws NamingException {
return lookupLink( new CompositeName( name ) );
}
/**
* Returns a list of objects bound to the context
*/
215 public NamingEnumeration list( Name name ) throws NamingException {
Name searchName = validateName( name );
// If null, it means we don't know how to handle this -> throw to the
// parent
if ( searchName == null )
return this.parent.list( name );
// If empty name, return a copy of this Context
else if ( searchName.isEmpty( ) ) {
NamingEnumeration e = null;
synchronized ( this.contextLock ) {
e = new WinstoneNameEnumeration( this.bindings );
}
return e;
}
// Lookup the object - if it's not a context, throw an error
else {
Object ctx = this.lookup( searchName );
if ( ctx instanceof Context )
try {
return ( ( Context ) ctx ).list( new CompositeName( "" ) );
} finally {
( ( Context ) ctx ).close( );
}
else if ( ctx == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", searchName.toString( ) ) );
else
throw new NotContextException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext",
new String[] { searchName.toString( ),
ctx.getClass( ).getName( ) } ) );
}
}
251 public NamingEnumeration list( String name ) throws NamingException {
return list( new CompositeName( name ) );
}
255 public NamingEnumeration listBindings( Name name ) throws NamingException {
Name searchName = validateName( name );
// If null, it means we don't know how to handle this -> throw to the
// parent
if ( searchName == null )
return this.parent.list( name );
// If empty name, return a copy of this Context
else if ( searchName.isEmpty( ) ) {
NamingEnumeration e = null;
synchronized ( this.contextLock ) {
e = new WinstoneBindingEnumeration( this.bindings,
this.environment, this );
}
return e;
}
// Lookup the object - if it's not a context, throw an error
else {
Object ctx = this.lookup( searchName );
if ( ctx instanceof Context )
try {
return ( ( Context ) ctx ).listBindings( new CompositeName( "" ) );
} finally {
( ( Context ) ctx ).close( );
}
else if ( ctx == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", searchName.toString( ) ) );
else
throw new NotContextException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext",
new String[] { searchName.toString( ),
ctx.getClass( ).getName( ) } ) );
}
}
292 public NamingEnumeration listBindings( String name ) throws NamingException {
return listBindings( new CompositeName( name ) );
}
296 public NameParser getNameParser( Name name ) throws NamingException {
Object obj = lookup( name );
if ( obj instanceof Context ) {
( ( Context ) obj ).close( );
}
return nameParser;
}
304 public NameParser getNameParser( String name ) throws NamingException {
return getNameParser( new CompositeName( name ) );
}
308 public String getNameInNamespace( ) throws NamingException {
return this.myAbsoluteName;
}
/***************************************************************************
* Below here is for read-write contexts ... *
**************************************************************************/
316 public void bind( String name, Object value ) throws NamingException {
bind( new CompositeName( name ), value );
}
320 public void bind( Name name, Object value ) throws NamingException {
bind( name, value, false );
}
324 protected void bind( Name name, Object value, boolean allowOverwrites )
throws NamingException {
Name bindName = validateName( name );
// If null, it means we don't know how to handle this -> throw to the
// parent
if ( bindName == null )
this.parent.bind( name, value, allowOverwrites );
// If empty name, complain - we should have a child name here
else if ( bindName.isEmpty( ) )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", name.toString( ) ) );
else if ( bindName.size( ) > 1 ) {
Object ctx = lookup( bindName.get( 0 ) );
if ( !( ctx instanceof Context ) )
throw new NotContextException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
bindName.get( 0 ), ctx.getClass( ).getName( ) } ) );
else if ( ctx == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", bindName.get( 0 ) ) );
else
try {
if ( allowOverwrites )
( ( Context ) ctx ).rebind( bindName.getSuffix( 1 ), value );
else
( ( Context ) ctx ).bind( bindName.getSuffix( 1 ), value );
} finally {
( ( Context ) ctx ).close( );
}
} else if ( ( !allowOverwrites ) && this.bindings.get( name.get( 0 ) ) != null )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", name.toString( ) ) );
else {
value = NamingManager.getStateToBind( value, new CompositeName( )
.add( bindName.get( 0 ) ), this, this.environment );
synchronized ( this.contextLock ) {
this.bindings.put( bindName.get( 0 ), value );
}
}
}
366 public void rebind( String name, Object value ) throws NamingException {
rebind( new CompositeName( name ), value );
}
370 public void rebind( Name name, Object value ) throws NamingException {
bind( name, value, true );
}
374 public void unbind( String name ) throws NamingException {
unbind( new CompositeName( name ) );
}
378 public void unbind( Name name ) throws NamingException {
Name unbindName = validateName( name );
// If null, it means we don't know how to handle this -> throw to the
// parent
if ( unbindName == null )
this.parent.unbind( name );
// If empty name, complain - we should have a child name here
else if ( unbindName.isEmpty( ) )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES
.getString( "WinstoneContext.CantUnbindEmptyName" ) );
else if ( unbindName.size( ) > 1 ) {
Object ctx = lookup( unbindName.get( 0 ) );
if ( !( ctx instanceof Context ) )
throw new NotContextException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
unbindName.get( 0 ), ctx.getClass( ).getName( ) } ) );
else if ( ctx == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", unbindName.get( 0 ) ) );
else
try {
( ( Context ) ctx ).unbind( unbindName.getSuffix( 1 ) );
} finally {
( ( Context ) ctx ).close( );
}
} else if ( this.bindings.get( name.get( 0 ) ) == null )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", name.toString( ) ) );
else {
synchronized ( this.contextLock ) {
// Object removing = this.bindings.get( unbindName.get( 0 ) );
this.bindings.remove( unbindName.get( 0 ) );
}
}
}
415 public void rename( Name oldName, Name newName ) throws NamingException {
throw new OperationNotSupportedException(
"rename not supported in Winstone java:/ context" );
}
420 public void rename( String oldName, String newName ) throws NamingException {
rename( new CompositeName( oldName ), new CompositeName( newName ) );
}
424 public Context createSubcontext( String name ) throws NamingException {
return createSubcontext( new CompositeName( name ) );
}
428 public Context createSubcontext( Name name ) throws NamingException {
Name childName = validateName( name );
// If null, it means we don't know how to handle this -> throw to the
// parent
if ( childName == null )
return this.parent.createSubcontext( name );
// If empty name, complain - we should have a child name here
else if ( childName.isEmpty( ) )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", name.toString( ) ) );
else if ( childName.size( ) > 1 ) {
Object ctx = lookup( childName.get( 0 ) );
if ( !( ctx instanceof Context ) )
throw new NotContextException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
childName.get( 0 ), ctx.getClass( ).getName( ) } ) );
else if ( ctx == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", childName.get( 0 ) ) );
else
try {
( ( Context ) ctx ).createSubcontext( childName.getSuffix( 1 ) );
} finally {
( ( Context ) ctx ).close( );
}
}
Context childContext = null;
synchronized ( this.contextLock ) {
if ( this.bindings.get( childName.get( 0 ) ) != null )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", childName.get( 0 ) ) );
else {
childContext = new WinstoneContext( this.environment, this,
this.myAbsoluteName + "/" + childName.get( 0 ),
new Boolean( true ) );
this.bindings.put( childName.get( 0 ), childContext );
}
}
return childContext;
}
471 public void destroySubcontext( String name ) throws NamingException {
destroySubcontext( new CompositeName( name ) );
}
475 public void destroySubcontext( Name name ) throws NamingException {
Name childName = validateName( name );
// If null, it means we don't know how to handle this -> throw to the parent
if ( childName == null )
this.parent.destroySubcontext( name );
// If absolutely referring to this context, tell the parent to delete this context
else if ( childName.isEmpty( ) ) {
if ( !name.isEmpty( ) )
this.parent.destroySubcontext( name.getSuffix( name.size( ) - 2 ) );
else
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES
.getString( "WinstoneContext.CantDestroyEmptyName" ) );
} else if ( childName.size( ) > 1 ) {
Object ctx = lookup( childName.get( 0 ) );
if ( !( ctx instanceof Context ) )
throw new NotContextException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
childName.get( 0 ), ctx.getClass( ).getName( ) } ) );
else if ( ctx == null )
throw new NameNotFoundException( ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", childName.get( 0 ) ) );
else
try {
( ( Context ) ctx ).destroySubcontext( childName.getSuffix( 1 ) );
} finally {
( ( Context ) ctx ).close( );
}
} else
synchronized ( this.contextLock ) {
Context childContext = ( Context ) lookup( childName.get( 0 ) );
childContext.close( );
this.bindings.remove( childName.get( 0 ) );
}
}
511 public String composeName( String name1, String name2 )
throws NamingException {
Name name = composeName( new CompositeName( name1 ), new CompositeName(
name2 ) );
return name == null ? null : name.toString( );
}
518 public Name composeName( Name name1, Name name2 ) throws NamingException {
throw new OperationNotSupportedException(
"composeName not supported in Winstone java:/ namespace" );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
/**
* Enumeration across the names/classes of the bindings in a particular context.
* Used by the list( ) method.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneNameEnumeration.java, v 1.3 2006/02/28 07:32:48 rickknowles Exp $
*/
25 public class WinstoneNameEnumeration implements NamingEnumeration {
26 private Enumeration nameEnumeration;
/**
* Constructor
*/
31 public WinstoneNameEnumeration( Map bindings ) {
Object keys[] = bindings.keySet( ).toArray( );
Arrays.sort( keys );
Vector nameClassPairs = new Vector( );
for ( int n = 0; n < keys.length; n++ )
nameClassPairs.add( new NameClassPair( ( String ) keys[n], bindings
.get( keys[n] ).getClass( ).getName( ) ) );
this.nameEnumeration = nameClassPairs.elements( );
}
41 public void close( ) throws NamingException {
this.nameEnumeration = null;
}
45 public boolean hasMore( ) throws NamingException {
if ( this.nameEnumeration == null )
throw new NamingException( ContainerJNDIManager.JNDI_RESOURCES
.getString( "WinstoneNameEnumeration.AlreadyClosed" ) );
else
return this.nameEnumeration.hasMoreElements( );
}
53 public Object next( ) throws NamingException {
if ( hasMore( ) )
return this.nameEnumeration.nextElement( );
else
return null;
}
60 public boolean hasMoreElements( ) {
try {
return hasMore( );
} catch ( NamingException err ) {
return false;
}
}
68 public Object nextElement( ) {
try {
return next( );
} catch ( NamingException err ) {
return null;
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi;
import java.util.Properties;
import javax.naming.CompoundName;
import javax.naming.Name;
import javax.naming.NameParser;
import javax.naming.NamingException;
/**
* The name parser for winstone jndi names
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneNameParser.java, v 1.2 2006/02/28 07:32:48 rickknowles Exp $
*/
22 public class WinstoneNameParser implements NameParser {
23 private static final Properties syntax = new Properties( );
static {
syntax.put( "jndi.syntax.direction", "left_to_right" );
syntax.put( "jndi.syntax.separator", "/" );
syntax.put( "jndi.syntax.ignorecase", "false" );
syntax.put( "jndi.syntax.escape", "\\" );
syntax.put( "jndi.syntax.beginquote", "'" );
}
32 public Name parse( String name ) throws NamingException {
return new CompoundName( name, syntax );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi.java;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import javax.naming.spi.ObjectFactory;
import winstone.jndi.WinstoneContext;
/**
* Creates the initial instance of the Winstone JNDI context ( corresponds to
* java:/ urls )
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: javaURLContextFactory.java, v 1.5 2007/04/23 02:55:35 rickknowles Exp $
*/
26 public class javaURLContextFactory implements InitialContextFactory, ObjectFactory {
28 private static WinstoneContext rootContext;
29 private Object lock = new Boolean( true );
31 public Context getInitialContext( Hashtable env ) throws NamingException {
synchronized ( lock ) {
if ( rootContext == null ) {
Object lock = new Boolean( true );
rootContext = new WinstoneContext( env, null, "java:", lock );
WinstoneContext compCtx = new WinstoneContext( env, rootContext, "java:/comp", lock );
WinstoneContext envCtx = new WinstoneContext( env, compCtx, "java:/comp/env", lock );
rootContext.rebind( "java:/comp", compCtx );
compCtx.rebind( "env", envCtx );
}
}
return ( Context ) rootContext.lookup( "java:/comp/env" );
}
45 public Object getObjectInstance( Object object, Name name, Context context,
46 Hashtable env ) {
return null;
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi.resourceFactories;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Map;
import winstone.Logger;
/**
* JDBC Connection wrapper for use in the pooling datasource. This just suppresses
* the close( ) call, and releases the connection.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneConnection.java, v 1.3 2006/02/28 07:32:48 rickknowles Exp $
*/
28 public class WinstoneConnection implements Connection {
29 private Connection realConnection;
30 private WinstoneDataSource datasource;
/**
* Constructor - this sets the real connection and the link back to the pool
*/
35 public WinstoneConnection( Connection connection,
36 WinstoneDataSource datasource ) {
this.realConnection = connection;
this.datasource = datasource;
}
41 public void close( ) throws SQLException {
if ( ( this.datasource != null ) && ( this.datasource.getLogWriter( ) != null ) ) {
this.datasource.getLogWriter( ).println(
WinstoneDataSource.DS_RESOURCES.getString(
"WinstoneConnection.ReleaseRollback" ) );
} else {
Logger.log( Logger.FULL_DEBUG, WinstoneDataSource.DS_RESOURCES,
"WinstoneConnection.ReleaseRollback" );
}
Connection realConnectionHolder = null;
try {
if ( this.realConnection != null ) {
realConnectionHolder = this.realConnection;
this.realConnection = null;
if ( !realConnectionHolder.getAutoCommit( ) )
realConnectionHolder.rollback( );
}
} finally {
if ( ( this.datasource != null ) && ( realConnectionHolder != null ) ) {
this.datasource.releaseConnection( this, realConnectionHolder );
this.datasource = null;
}
}
}
68 public boolean isClosed( ) throws SQLException {
return ( this.realConnection == null );
}
72 public void commit( ) throws SQLException {
this.realConnection.commit( );
}
76 public void rollback( ) throws SQLException {
this.realConnection.rollback( );
}
80 public void rollback( Savepoint sp ) throws SQLException {
this.realConnection.rollback( sp );
}
84 public boolean getAutoCommit( ) throws SQLException {
return this.realConnection.getAutoCommit( );
}
88 public void setAutoCommit( boolean autoCommit ) throws SQLException {
this.realConnection.setAutoCommit( autoCommit );
}
92 public int getHoldability( ) throws SQLException {
return this.realConnection.getHoldability( );
}
96 public void setHoldability( int hold ) throws SQLException {
this.realConnection.setHoldability( hold );
}
100 public int getTransactionIsolation( ) throws SQLException {
return this.realConnection.getTransactionIsolation( );
}
104 public void setTransactionIsolation( int level ) throws SQLException {
this.realConnection.setTransactionIsolation( level );
}
108 public void clearWarnings( ) throws SQLException {
this.realConnection.clearWarnings( );
}
112 public SQLWarning getWarnings( ) throws SQLException {
return this.realConnection.getWarnings( );
}
116 public boolean isReadOnly( ) throws SQLException {
return this.realConnection.isReadOnly( );
}
120 public void setReadOnly( boolean ro ) throws SQLException {
this.realConnection.setReadOnly( ro );
}
124 public String getCatalog( ) throws SQLException {
return this.realConnection.getCatalog( );
}
128 public void setCatalog( String catalog ) throws SQLException {
this.realConnection.setCatalog( catalog );
}
132 public DatabaseMetaData getMetaData( ) throws SQLException {
return this.realConnection.getMetaData( );
}
136 public Savepoint setSavepoint( ) throws SQLException {
return this.realConnection.setSavepoint( );
}
140 public Savepoint setSavepoint( String name ) throws SQLException {
return this.realConnection.setSavepoint( name );
}
144 public void releaseSavepoint( Savepoint sp ) throws SQLException {
this.realConnection.releaseSavepoint( sp );
}
148 public Map getTypeMap( ) throws SQLException {
return this.realConnection.getTypeMap( );
}
152 public void setTypeMap( Map map ) throws SQLException {
this.realConnection.setTypeMap( map );
}
156 public String nativeSQL( String sql ) throws SQLException {
return this.realConnection.nativeSQL( sql );
}
160 public CallableStatement prepareCall( String sql ) throws SQLException {
return this.realConnection.prepareCall( sql );
}
164 public CallableStatement prepareCall( String sql, int resultSetType,
int resultSetConcurrency ) throws SQLException {
return this.realConnection.prepareCall( sql, resultSetType,
resultSetConcurrency );
}
170 public CallableStatement prepareCall( String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability )
throws SQLException {
return this.realConnection.prepareCall( sql, resultSetType,
resultSetConcurrency, resultSetHoldability );
}
177 public Statement createStatement( ) throws SQLException {
return this.realConnection.createStatement( );
}
181 public Statement createStatement( int resultSetType, int resultSetConcurrency )
throws SQLException {
return this.realConnection.createStatement( resultSetType,
resultSetConcurrency );
}
187 public Statement createStatement( int resultSetType,
int resultSetConcurrency, int resultSetHoldability )
throws SQLException {
return this.realConnection.createStatement( resultSetType,
resultSetConcurrency, resultSetHoldability );
}
194 public PreparedStatement prepareStatement( String sql ) throws SQLException {
return this.realConnection.prepareStatement( sql );
}
198 public PreparedStatement prepareStatement( String sql, int autogeneratedKeys )
throws SQLException {
return this.realConnection.prepareStatement( sql, autogeneratedKeys );
}
203 public PreparedStatement prepareStatement( String sql, int resultSetType,
int resultSetConcurrency ) throws SQLException {
return this.realConnection.prepareStatement( sql, resultSetType,
resultSetConcurrency );
}
209 public PreparedStatement prepareStatement( String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability )
throws SQLException {
return this.realConnection.prepareStatement( sql, resultSetType,
resultSetConcurrency, resultSetHoldability );
}
216 public PreparedStatement prepareStatement( String sql, int[] columnIndexes )
throws SQLException {
return this.realConnection.prepareStatement( sql, columnIndexes );
}
221 public PreparedStatement prepareStatement( String sql, String[] columnNames )
throws SQLException {
return this.realConnection.prepareStatement( sql, columnNames );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi.resourceFactories;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
/**
* Implements a JDBC 2.0 pooling datasource. This is meant to act as a wrapper
* around a JDBC 1.0 driver, just providing the pool management functions.
*
* Supports keep alives, and check-connection-before-get options, as well
* as normal reclaimable pool management options like maxIdle, maxConnections and
* startConnections. Additionally it supports poll-retry on full, which means the
* getConnection call will block and retry after a certain period when the pool
* is maxed out ( good for high load conditions ).
*
* This class was originally drawn from the generator-runtime servlet framework and
* modified to make it more JDBC-API only compliant.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneDataSource.java, v 1.8 2006/11/07 01:30:39 rickknowles Exp $
*/
42 public class WinstoneDataSource implements DataSource, Runnable {
44 public static final WinstoneResourceBundle DS_RESOURCES =
new WinstoneResourceBundle( "winstone.jndi.resourceFactories.LocalStrings" );
47 private String name;
49 private String url;
50 private Driver driver;
51 private Properties connectProps;
private int maxIdleCount;
private int maxHeldCount;
private int retryCount;
private int retryPeriod;
58 private String keepAliveSQL;
private int keepAlivePeriod;
private boolean checkBeforeGet;
private int killInactivePeriod;
63 private List usedWrappers;
64 private List unusedRealConnections; // sempahore
65 private List usedRealConnections;
67 private Thread managementThread;
private int loginTimeout;
70 private PrintWriter logWriter;
/**
* Main constructor. Basically just calls the init method
*/
75 public WinstoneDataSource( String name, Map args, ClassLoader loader ) {
this.name = name;
this.usedWrappers = new ArrayList( );
this.usedRealConnections = new ArrayList( );
this.unusedRealConnections = new ArrayList( );
this.connectProps = new Properties( );
// Extract pool management properties
this.keepAliveSQL = WebAppConfiguration.stringArg( args, "keepAliveSQL", "" );
this.keepAlivePeriod = WebAppConfiguration.intArg( args, "keepAlivePeriod", -1 );
this.checkBeforeGet = WebAppConfiguration.booleanArg( args, "checkBeforeGet",
!this.keepAliveSQL.equals( "" ) );
this.killInactivePeriod = WebAppConfiguration.intArg( args, "killInactivePeriod", -1 );
this.url = WebAppConfiguration.stringArg( args, "url", null );
String driverClassName = WebAppConfiguration.stringArg( args, "driverClassName", "" );
if ( args.get( "username" ) != null )
this.connectProps.put( "user", args.get( "username" ) );
if ( args.get( "password" ) != null )
this.connectProps.put( "password", args.get( "password" ) );
this.maxHeldCount = WebAppConfiguration.intArg( args, "maxConnections", 20 );
this.maxIdleCount = WebAppConfiguration.intArg( args, "maxIdle", 10 );
int startCount = WebAppConfiguration.intArg( args, "startConnections", 1 );
this.retryCount = WebAppConfiguration.intArg( args, "retryCount", 1 );
this.retryPeriod = WebAppConfiguration.intArg( args, "retryPeriod", 1000 );
log( Logger.FULL_DEBUG, "WinstoneDataSource.Init", this.url, null );
try {
synchronized ( this.unusedRealConnections ) {
if ( !driverClassName.equals( "" ) ) {
Class driverClass = Class.forName( driverClassName.trim( ), true, loader );
this.driver = ( Driver ) driverClass.newInstance( );
for ( int n = 0; n < startCount; n++ ) {
makeNewRealConnection( this.unusedRealConnections );
}
}
}
} catch ( Throwable err ) {
log( Logger.ERROR, "WinstoneDataSource.ErrorInCreate", this.name, err );
}
// Start management thread
this.managementThread = new Thread( this, "DBConnectionPool management thread" );
this.managementThread.start( );
}
/**
* Close this pool - probably because we will want to re-init the pool
*/
129 public void destroy( ) {
if ( this.managementThread != null ) {
this.managementThread.interrupt( );
this.managementThread = null;
}
synchronized ( this.unusedRealConnections ) {
killPooledConnections( this.unusedRealConnections, 0 );
killPooledConnections( this.usedRealConnections, 0 );
}
this.usedRealConnections.clear( );
this.unusedRealConnections.clear( );
this.usedWrappers.clear( );
}
/**
* Gets a connection with a specific username/password. These are not pooled.
*/
148 public Connection getConnection( String username, String password )
throws SQLException {
Properties newProps = new Properties( );
newProps.put( "user", username );
newProps.put( "password", password );
Connection conn = this.driver.connect( this.url, newProps );
WinstoneConnection wrapper = null;
synchronized ( this.unusedRealConnections ) {
wrapper = new WinstoneConnection( conn, this );
this.usedWrappers.add( wrapper );
}
return wrapper;
}
162 public Connection getConnection( ) throws SQLException {
return getConnection( this.retryCount );
}
/**
* Get a read-write connection - preferably from the pool, but fresh if needed
*/
169 protected Connection getConnection( int retriesAllowed ) throws SQLException {
Connection realConnection = null;
synchronized ( this.unusedRealConnections ) {
// If we have any spare, get it from the unused pool
if ( this.unusedRealConnections.size( ) > 0 ) {
realConnection = ( Connection ) this.unusedRealConnections.get( 0 );
this.unusedRealConnections.remove( realConnection );
this.usedRealConnections.add( realConnection );
log( Logger.FULL_DEBUG, "WinstoneDataSource.UsingPooled",
new String[] {"" + this.usedRealConnections.size( ),
"" + this.unusedRealConnections.size( )}, null );
try {
return prepareWrapper( realConnection );
} catch ( SQLException err ) {
// Leave the realConnection as non-null, so we know prepareWrapper failed
}
}
// If we are out ( and not over our limit ), allocate a new one
else if ( this.usedRealConnections.size( ) < maxHeldCount ) {
realConnection = makeNewRealConnection( this.usedRealConnections );
log( Logger.FULL_DEBUG, "WinstoneDataSource.UsingNew",
new String[] {"" + this.usedRealConnections.size( ),
"" + this.unusedRealConnections.size( )}, null );
try {
return prepareWrapper( realConnection );
} catch ( SQLException err ) {
// Leave the realConnection as non-null, so we know prepareWrapper failed
}
}
}
if ( realConnection != null ) {
// prepareWrapper( ) must have failed, so call this method again
realConnection = null;
return getConnection( retriesAllowed );
} else if ( retriesAllowed <= 0 ) {
// otherwise throw fail message - we've blown our limit
throw new SQLException( DS_RESOURCES.getString( "WinstoneDataSource.Exceeded", "" + maxHeldCount ) );
} else {
log( Logger.FULL_DEBUG, "WinstoneDataSource.Retrying", new String[] {
"" + maxHeldCount, "" + retriesAllowed, "" + retryPeriod}, null );
// If we are here, it's because we need to retry for a connection
try {
Thread.sleep( retryPeriod );
} catch ( InterruptedException err ) {}
return getConnection( retriesAllowed - 1 );
}
}
221 private Connection prepareWrapper( Connection realConnection ) throws SQLException {
// Check before get( )
if ( this.checkBeforeGet ) {
try {
executeKeepAlive( realConnection );
} catch ( SQLException err ) {
// Dead connection, kill it and try again
killConnection( this.usedRealConnections, realConnection );
throw err;
}
}
realConnection.setAutoCommit( false );
WinstoneConnection wrapper = new WinstoneConnection( realConnection, this );
this.usedWrappers.add( wrapper );
return wrapper;
}
/**
* Releases connections back to the pool
*/
241 void releaseConnection( WinstoneConnection wrapper, Connection realConnection ) throws SQLException {
synchronized ( this.unusedRealConnections ) {
if ( wrapper != null ) {
this.usedWrappers.remove( wrapper );
}
if ( realConnection != null ) {
if ( this.usedRealConnections.contains( realConnection ) ) {
this.usedRealConnections.remove( realConnection );
this.unusedRealConnections.add( realConnection );
log( Logger.FULL_DEBUG, "WinstoneDataSource.Releasing",
new String[] {"" + this.usedRealConnections.size( ),
"" + this.unusedRealConnections.size( )}, null );
} else {
log( Logger.WARNING, "WinstoneDataSource.ReleasingUnknown", null );
realConnection.close( );
}
}
}
}
261 public int getLoginTimeout( ) {
return this.loginTimeout;
}
265 public PrintWriter getLogWriter( ) {
return this.logWriter;
}
269 public void setLoginTimeout( int timeout ) {
this.loginTimeout = timeout;
}
273 public void setLogWriter( PrintWriter writer ) {
this.logWriter = writer;
}
/**
* Clean up and keep-alive thread.
* Note - this needs a lot more attention to the semaphore use during keepAlive etc
*/
281 public void run( ) {
log( Logger.DEBUG, "WinstoneDataSource.MaintenanceStart", null );
int keepAliveCounter = -1;
int killInactiveCounter = -1;
boolean threadRunning = true;
while ( threadRunning ) {
try {
long startTime = System.currentTimeMillis( );
// Keep alive if the time is right
if ( ( this.keepAlivePeriod != -1 ) && threadRunning ) {
keepAliveCounter++;
if ( this.keepAlivePeriod <= keepAliveCounter ) {
synchronized ( this.unusedRealConnections ) {
executeKeepAliveOnUnused( );
}
keepAliveCounter = 0;
}
}
if ( Thread.interrupted( ) ) {
threadRunning = false;
}
// Kill inactive connections if the time is right
if ( ( this.killInactivePeriod != -1 ) && threadRunning ) {
killInactiveCounter++;
if ( this.killInactivePeriod <= killInactiveCounter ) {
synchronized ( this.unusedRealConnections ) {
killPooledConnections( this.unusedRealConnections, this.maxIdleCount );
}
killInactiveCounter = 0;
}
}
if ( ( killInactiveCounter == 0 ) || ( keepAliveCounter == 0 ) ) {
log( Logger.FULL_DEBUG, "WinstoneDataSource.MaintenanceTime",
"" + ( System.currentTimeMillis( ) - startTime ), null );
}
if ( Thread.interrupted( ) ) {
threadRunning = false;
} else {
Thread.sleep( 60000 ); // sleep 1 minute
}
if ( Thread.interrupted( ) ) {
threadRunning = false;
}
} catch ( InterruptedException err ) {
threadRunning = false;
continue;
}
}
log( Logger.DEBUG, "WinstoneDataSource.MaintenanceFinish", null );
}
/**
* Executes keep alive for each of the connections in the supplied pool
*/
347 protected void executeKeepAliveOnUnused( ) {
// keep alive all unused roConns now
List dead = new ArrayList( );
for ( Iterator i = this.unusedRealConnections.iterator( ); i.hasNext( ); ) {
Connection conn = ( Connection ) i.next( );
try {
executeKeepAlive( conn );
} catch ( SQLException errSQL ) {
dead.add( conn );
}
}
for ( Iterator i = dead.iterator( ); i.hasNext( ); ) {
killConnection( this.unusedRealConnections, ( Connection ) i.next( ) );
}
log( Logger.FULL_DEBUG, "WinstoneDataSource.KeepAliveFinished", "" +
this.unusedRealConnections.size( ), null );
}
369 protected void executeKeepAlive( Connection connection ) throws SQLException {
if ( !this.keepAliveSQL.equals( "" ) ) {
PreparedStatement qryKeepAlive = null;
try {
qryKeepAlive = connection.prepareStatement( keepAliveSQL );
qryKeepAlive.execute( );
} catch ( SQLException err ) {
log( Logger.WARNING, "WinstoneDataSource.KeepAliveFailed", err );
throw err;
} finally {
if ( qryKeepAlive != null ) {
qryKeepAlive.close( );
}
}
}
}
/**
* This makes a new rw connection. It assumes that the synchronization has taken
* place in the calling code, so is unsafe for use outside this class.
*/
390 protected Connection makeNewRealConnection( List pool ) throws SQLException {
if ( this.url == null ) {
throw new SQLException( "No JDBC URL supplied" );
}
Connection realConnection = this.driver.connect( this.url, this.connectProps );
pool.add( realConnection );
log( Logger.FULL_DEBUG, "WinstoneDataSource.AddingNew",
new String[] {"" + this.usedRealConnections.size( ),
"" + this.unusedRealConnections.size( )}, null );
return realConnection;
}
/**
* Iterates through a list and kills off unused connections until we reach the
* minimum idle count for that pool.
*/
408 protected void killPooledConnections( List pool, int maxIdleCount ) {
// kill inactive unused roConns now
int killed = 0;
while ( pool.size( ) > maxIdleCount ) {
killed++;
Connection conn = ( Connection ) pool.get( 0 );
killConnection( pool, conn );
}
if ( killed > 0 ) {
log( Logger.FULL_DEBUG, "WinstoneDataSource.Killed", "" + killed, null );
}
}
423 private static void killConnection( List pool, Connection conn ) {
pool.remove( conn );
try {
conn.close( );
} catch ( SQLException err ) {
}
}
431 private void log( int level, String msgKey, Throwable err ) {
if ( getLogWriter( ) != null ) {
getLogWriter( ).println( DS_RESOURCES.getString( msgKey ) );
if ( err != null ) {
err.printStackTrace( getLogWriter( ) );
}
} else {
Logger.log( level, DS_RESOURCES, msgKey, err );
}
}
442 private void log( int level, String msgKey, String arg, Throwable err ) {
if ( getLogWriter( ) != null ) {
getLogWriter( ).println( DS_RESOURCES.getString( msgKey, arg ) );
if ( err != null ) {
err.printStackTrace( getLogWriter( ) );
}
} else {
Logger.log( level, DS_RESOURCES, msgKey, arg, err );
}
}
453 private void log( int level, String msgKey, String arg[], Throwable err ) {
if ( getLogWriter( ) != null ) {
getLogWriter( ).println( DS_RESOURCES.getString( msgKey, arg ) );
if ( err != null ) {
err.printStackTrace( getLogWriter( ) );
}
} else {
Logger.log( level, DS_RESOURCES, msgKey, arg, err );
}
}
464 public String toString( ) {
return DS_RESOURCES.getString( "WinstoneDataSource.StatusMsg",
new String[] { this.name,
"" + this.usedRealConnections.size( ),
"" + this.unusedRealConnections.size( )} );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.realm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import winstone.AuthenticationPrincipal;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
/**
* Base class for authentication realms. Subclasses provide the source of
* authentication roles, usernames, passwords, etc, and when asked for
* validation respond with a role if valid, or null otherwise.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: ArgumentsRealm.java, v 1.4 2007/06/01 15:55:41 rickknowles Exp $
*/
32 public class ArgumentsRealm implements AuthenticationRealm {
33 private static final WinstoneResourceBundle REALM_RESOURCES = new WinstoneResourceBundle( "winstone.realm.LocalStrings" );
35 static final String PASSWORD_PREFIX = "argumentsRealm.passwd.";
36 static final String ROLES_PREFIX = "argumentsRealm.roles.";
37 private Map passwords;
38 private Map roles;
/**
* Constructor - this sets up an authentication realm, using the arguments
* supplied on the command line as a source of userNames/passwords/roles.
*/
44 public ArgumentsRealm( Set rolesAllowed, Map args ) {
this.passwords = new Hashtable( );
this.roles = new Hashtable( );
for ( Iterator i = args.keySet( ).iterator( ); i.hasNext( ); ) {
String key = ( String ) i.next( );
if ( key.startsWith( PASSWORD_PREFIX ) ) {
String userName = key.substring( PASSWORD_PREFIX.length( ) );
String password = ( String ) args.get( key );
String roleList = WebAppConfiguration.stringArg( args, ROLES_PREFIX + userName, "" );
if ( roleList.equals( "" ) ) {
Logger.log( Logger.WARNING, REALM_RESOURCES, "ArgumentsRealm.UndeclaredRoles", userName );
} else {
StringTokenizer st = new StringTokenizer( roleList, ", " );
List rl = new ArrayList( );
for ( ; st.hasMoreTokens( ); ) {
String currentRole = st.nextToken( );
if ( rolesAllowed.contains( currentRole ) )
rl.add( currentRole );
}
Object roleArray[] = rl.toArray( );
Arrays.sort( roleArray );
this.roles.put( userName, Arrays.asList( roleArray ) );
}
this.passwords.put( userName, password );
}
}
Logger.log( Logger.DEBUG, REALM_RESOURCES, "ArgumentsRealm.Initialised",
"" + this.passwords.size( ) );
}
/**
* Authenticate the user - do we know them ? Return a principal once we know
* them
*/
81 public AuthenticationPrincipal authenticateByUsernamePassword(
82 String userName, String password ) {
if ( ( userName == null ) || ( password == null ) )
return null;
String realPassword = ( String ) this.passwords.get( userName );
if ( realPassword == null )
return null;
else if ( !realPassword.equals( password ) )
return null;
else
return new AuthenticationPrincipal( userName, password,
( List ) this.roles.get( userName ) );
}
/**
* Retrieve an authenticated user
*/
99 public AuthenticationPrincipal retrieveUser( String userName ) {
if ( userName == null )
return null;
else
return new AuthenticationPrincipal( userName,
( String ) this.passwords.get( userName ), ( List ) this.roles
.get( userName ) );
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.realm;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import winstone.AuthenticationPrincipal;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WinstoneException;
import winstone.WinstoneResourceBundle;
/**
* @author rickk
* @version $Id: FileRealm.java, v 1.4 2006/08/30 04:07:52 rickknowles Exp $
*/
36 public class FileRealm implements AuthenticationRealm {
37 private static final WinstoneResourceBundle REALM_RESOURCES = new WinstoneResourceBundle( "winstone.realm.LocalStrings" );
39 final String FILE_NAME_ARGUMENT = "fileRealm.configFile";
40 final String DEFAULT_FILE_NAME = "users.xml";
41 final String ELEM_USER = "user";
42 final String ATT_USERNAME = "username";
43 final String ATT_PASSWORD = "password";
44 final String ATT_ROLELIST = "roles";
45 private Map passwords;
46 private Map roles;
/**
* Constructor - this sets up an authentication realm, using the file
* supplied on the command line as a source of userNames/passwords/roles.
*/
52 public FileRealm( Set rolesAllowed, Map args ) {
this.passwords = new Hashtable( );
this.roles = new Hashtable( );
// Get the filename and parse the xml doc
String realmFileName = args.get( FILE_NAME_ARGUMENT ) == null ? DEFAULT_FILE_NAME
: ( String ) args.get( FILE_NAME_ARGUMENT );
File realmFile = new File( realmFileName );
if ( !realmFile.exists( ) )
throw new WinstoneException( REALM_RESOURCES.getString(
"FileRealm.FileNotFound", realmFile.getPath( ) ) );
try {
InputStream inFile = new FileInputStream( realmFile );
Document doc = this.parseStreamToXML( inFile );
inFile.close( );
Node rootElm = doc.getDocumentElement( );
for ( int n = 0; n < rootElm.getChildNodes( ).getLength( ); n++ ) {
Node child = rootElm.getChildNodes( ).item( n );
if ( ( child.getNodeType( ) == Node.ELEMENT_NODE )
&& ( child.getNodeName( ).equals( ELEM_USER ) ) ) {
String userName = null;
String password = null;
String roleList = null;
// Loop through for attributes
for ( int j = 0; j < child.getAttributes( ).getLength( ); j++ ) {
Node thisAtt = child.getAttributes( ).item( j );
if ( thisAtt.getNodeName( ).equals( ATT_USERNAME ) )
userName = thisAtt.getNodeValue( );
else if ( thisAtt.getNodeName( ).equals( ATT_PASSWORD ) )
password = thisAtt.getNodeValue( );
else if ( thisAtt.getNodeName( ).equals( ATT_ROLELIST ) )
roleList = thisAtt.getNodeValue( );
}
if ( ( userName == null ) || ( password == null )
|| ( roleList == null ) )
Logger.log( Logger.FULL_DEBUG, REALM_RESOURCES,
"FileRealm.SkippingUser", userName );
else {
// Parse the role list into an array and sort it
StringTokenizer st = new StringTokenizer( roleList, ", " );
List rl = new ArrayList( );
for ( ; st.hasMoreTokens( ); ) {
String currentRole = st.nextToken( );
if ( rolesAllowed.contains( currentRole ) )
rl.add( currentRole );
}
Object roleArray[] = rl.toArray( );
Arrays.sort( roleArray );
this.passwords.put( userName, password );
this.roles.put( userName, Arrays.asList( roleArray ) );
}
}
}
Logger.log( Logger.DEBUG, REALM_RESOURCES, "FileRealm.Initialised",
"" + this.passwords.size( ) );
} catch ( java.io.IOException err ) {
throw new WinstoneException( REALM_RESOURCES
.getString( "FileRealm.ErrorLoading" ), err );
}
}
/**
* Get a parsed XML DOM from the given inputstream. Used to process the
* web.xml application deployment descriptors.
*/
119 private Document parseStreamToXML( InputStream in ) {
try {
// Use JAXP to create a document builder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance( );
factory.setExpandEntityReferences( false );
factory.setValidating( false );
factory.setNamespaceAware( false );
factory.setIgnoringComments( true );
factory.setCoalescing( true );
factory.setIgnoringElementContentWhitespace( true );
DocumentBuilder builder = factory.newDocumentBuilder( );
return builder.parse( in );
} catch ( Throwable errParser ) {
throw new WinstoneException( REALM_RESOURCES
.getString( "FileRealm.XMLParseError" ), errParser );
}
}
/**
* Authenticate the user - do we know them ? Return a principal once we know
* them
*/
141 public AuthenticationPrincipal authenticateByUsernamePassword(
142 String userName, String password ) {
if ( ( userName == null ) || ( password == null ) )
return null;
String realPassword = ( String ) this.passwords.get( userName );
if ( realPassword == null )
return null;
else if ( !realPassword.equals( password ) )
return null;
else
return new AuthenticationPrincipal( userName, password,
( List ) this.roles.get( userName ) );
}
/**
* Retrieve an authenticated user
*/
159 public AuthenticationPrincipal retrieveUser( String userName ) {
return new AuthenticationPrincipal( userName, ( String ) this.passwords
.get( userName ), ( List ) this.roles.get( userName ) );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.ssl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import winstone.HostGroup;
import winstone.HttpListener;
import winstone.Logger;
import winstone.ObjectPool;
import winstone.WebAppConfiguration;
import winstone.WinstoneException;
import winstone.WinstoneRequest;
import winstone.WinstoneResourceBundle;
/**
* Implements the main listener daemon thread. This is the class that gets
* launched by the command line, and owns the server socket, etc.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: HttpsListener.java, v 1.10 2007/06/13 15:27:35 rickknowles Exp $
*/
45 public class HttpsListener extends HttpListener {
46 private static final WinstoneResourceBundle SSL_RESOURCES = new WinstoneResourceBundle( "winstone.ssl.LocalStrings" );
47 private String keystore;
48 private String password;
49 private String keyManagerType;
/**
* Constructor
*/
54 public HttpsListener( Map args, ObjectPool objectPool, HostGroup hostGroup ) throws IOException {
super( args, objectPool, hostGroup );
this.keystore = WebAppConfiguration.stringArg( args, getConnectorName( )
+ "KeyStore", "winstone.ks" );
this.password = WebAppConfiguration.stringArg( args, getConnectorName( )
+ "KeyStorePassword", null );
this.keyManagerType = WebAppConfiguration.stringArg( args,
getConnectorName( ) + "KeyManagerType", "SunX509" );
}
/**
* The default port to use - this is just so that we can override for the
* SSL connector.
*/
68 protected int getDefaultPort( ) {
return -1; // https disabled by default
}
/**
* The name to use when getting properties - this is just so that we can
* override for the SSL connector.
*/
76 protected String getConnectorScheme( ) {
return "https";
}
/**
* Gets a server socket - this gets as SSL socket instead of the standard
* socket returned in the base class.
*/
84 protected ServerSocket getServerSocket( ) throws IOException {
// Just to make sure it's set before we start
SSLContext context = getSSLContext( this.keystore, this.password );
SSLServerSocketFactory factory = context.getServerSocketFactory( );
SSLServerSocket ss = ( SSLServerSocket ) ( this.listenAddress == null ? factory
.createServerSocket( this.listenPort, BACKLOG_COUNT )
: factory.createServerSocket( this.listenPort, BACKLOG_COUNT,
InetAddress.getByName( this.listenAddress ) ) );
ss.setEnableSessionCreation( true );
ss.setWantClientAuth( true );
return ss;
}
/**
* Extracts the relevant socket stuff and adds it to the request object.
* This method relies on the base class for everything other than SSL
* related attributes
*/
102 protected void parseSocketInfo( Socket socket, WinstoneRequest req )
throws IOException {
super.parseSocketInfo( socket, req );
if ( socket instanceof SSLSocket ) {
SSLSocket s = ( SSLSocket ) socket;
SSLSession ss = s.getSession( );
if ( ss != null ) {
Certificate certChain[] = null;
try {
certChain = ss.getPeerCertificates( );
} catch ( Throwable err ) {/* do nothing */
}
if ( certChain != null ) {
req.setAttribute( "javax.servlet.request.X509Certificate",
certChain );
req.setAttribute( "javax.servlet.request.cipher_suite", ss
.getCipherSuite( ) );
req.setAttribute( "javax.servlet.request.ssl_session",
new String( ss.getId( ) ) );
req.setAttribute( "javax.servlet.request.key_size",
getKeySize( ss.getCipherSuite( ) ) );
}
}
req.setIsSecure( true );
}
}
/**
* Just a mapping of key sizes for cipher types. Taken indirectly from the
* TLS specs.
*/
134 private Integer getKeySize( String cipherSuite ) {
if ( cipherSuite.indexOf( "_WITH_NULL_" ) != -1 )
return new Integer( 0 );
else if ( cipherSuite.indexOf( "_WITH_IDEA_CBC_" ) != -1 )
return new Integer( 128 );
else if ( cipherSuite.indexOf( "_WITH_RC2_CBC_40_" ) != -1 )
return new Integer( 40 );
else if ( cipherSuite.indexOf( "_WITH_RC4_40_" ) != -1 )
return new Integer( 40 );
else if ( cipherSuite.indexOf( "_WITH_RC4_128_" ) != -1 )
return new Integer( 128 );
else if ( cipherSuite.indexOf( "_WITH_DES40_CBC_" ) != -1 )
return new Integer( 40 );
else if ( cipherSuite.indexOf( "_WITH_DES_CBC_" ) != -1 )
return new Integer( 56 );
else if ( cipherSuite.indexOf( "_WITH_3DES_EDE_CBC_" ) != -1 )
return new Integer( 168 );
else
return null;
}
/**
* Used to get the base ssl context in which to create the server socket.
* This is basically just so we can have a custom location for key stores.
*/
159 public SSLContext getSSLContext( String keyStoreName, String password )
throws IOException {
try {
// Check the key manager factory
KeyManagerFactory kmf = KeyManagerFactory.getInstance( this.keyManagerType );
File ksFile = new File( keyStoreName );
if ( !ksFile.exists( ) || !ksFile.isFile( ) )
throw new WinstoneException( SSL_RESOURCES.getString(
"HttpsListener.KeyStoreNotFound", ksFile.getPath( ) ) );
InputStream in = new FileInputStream( ksFile );
char[] passwordChars = password == null ? null : password.toCharArray( );
KeyStore ks = KeyStore.getInstance( "JKS" );
ks.load( in, passwordChars );
kmf.init( ks, passwordChars );
Logger.log( Logger.FULL_DEBUG, SSL_RESOURCES,
"HttpsListener.KeyCount", ks.size( ) + "" );
for ( Enumeration e = ks.aliases( ); e.hasMoreElements( ); ) {
String alias = ( String ) e.nextElement( );
Logger.log( Logger.FULL_DEBUG, SSL_RESOURCES,
"HttpsListener.KeyFound", new String[] { alias,
ks.getCertificate( alias ) + "" } );
}
SSLContext context = SSLContext.getInstance( "SSL" );
context.init( kmf.getKeyManagers( ), null, null );
Arrays.fill( passwordChars, 'x' );
return context;
} catch ( IOException err ) {
throw err;
} catch ( Throwable err ) {
throw new WinstoneException( SSL_RESOURCES
.getString( "HttpsListener.ErrorGettingContext" ), err );
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.tools;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import winstone.Launcher;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
/**
* Included so that we can control winstone from the command line a little more
* easily.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneControl.java, v 1.6 2006/03/13 15:37:29 rickknowles Exp $
*/
27 public class WinstoneControl {
28 private final static WinstoneResourceBundle TOOLS_RESOURCES = new WinstoneResourceBundle( "winstone.tools.LocalStrings" );
30 final static String OPERATION_SHUTDOWN = "shutdown";
31 final static String OPERATION_RELOAD = "reload:";
static int TIMEOUT = 10000;
/**
* Parses command line parameters, and calls the appropriate method for
* executing the winstone operation required.
*/
38 public static void main( String argv[] ) throws Exception {
// Load args from the config file
Map options = Launcher.loadArgsFromCommandLineAndConfig( argv, "operation" );
String operation = ( String ) options.get( "operation" );
if ( options.containsKey( "controlPort" ) && !options.containsKey( "port" ) ) {
options.put( "port", options.get( "controlPort" ) );
}
if ( operation.equals( "" ) ) {
printUsage( );
return;
}
Logger.setCurrentDebugLevel( Integer.parseInt( WebAppConfiguration
.stringArg( options, "debug", "5" ) ) );
String host = WebAppConfiguration.stringArg( options, "host", "localhost" );
String port = WebAppConfiguration.stringArg( options, "port", "8081" );
Logger.log( Logger.INFO, TOOLS_RESOURCES, "WinstoneControl.UsingHostPort",
new String[] { host, port } );
// Check for shutdown
if ( operation.equalsIgnoreCase( OPERATION_SHUTDOWN ) ) {
Socket socket = new Socket( host, Integer.parseInt( port ) );
socket.setSoTimeout( TIMEOUT );
OutputStream out = socket.getOutputStream( );
out.write( Launcher.SHUTDOWN_TYPE );
out.close( );
Logger.log( Logger.INFO, TOOLS_RESOURCES, "WinstoneControl.ShutdownOK",
new String[] { host, port } );
}
// check for reload
else if ( operation.toLowerCase( ).startsWith( OPERATION_RELOAD.toLowerCase( ) ) ) {
String webappName = operation.substring( OPERATION_RELOAD.length( ) );
Socket socket = new Socket( host, Integer.parseInt( port ) );
socket.setSoTimeout( TIMEOUT );
OutputStream out = socket.getOutputStream( );
out.write( Launcher.RELOAD_TYPE );
ObjectOutputStream objOut = new ObjectOutputStream( out );
objOut.writeUTF( host );
objOut.writeUTF( webappName );
objOut.close( );
out.close( );
Logger.log( Logger.INFO, TOOLS_RESOURCES, "WinstoneControl.ReloadOK",
new String[] { host, port } );
}
else {
printUsage( );
}
}
/**
* Displays the usage message
*/
95 private static void printUsage( ) throws IOException {
System.out.println( TOOLS_RESOURCES.getString( "WinstoneControl.Usage" ) );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testApplication.filters;
import java.io.IOException;
import java.util.Enumeration;
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;
/**
* Simple timing and request dumping test filter
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: TimingFilter.java, v 1.2 2006/02/28 07:32:50 rickknowles Exp $
*/
26 public class TimingFilter implements Filter {
private boolean dumpRequestParams;
29 private ServletContext context;
31 public void init( FilterConfig config ) {
String dumpRequestParams = config
.getInitParameter( "dumpRequestParameters" );
this.dumpRequestParams = ( ( dumpRequestParams != null ) && dumpRequestParams
.equalsIgnoreCase( "true" ) );
this.context = config.getServletContext( );
}
39 public void destroy( ) {
this.context = null;
}
/**
* Times the execution of the rest of the filter chain, optionally dumping
* the request parameters to the servlet context log
*/
47 public void doFilter( ServletRequest request, ServletResponse response,
48 FilterChain chain ) throws IOException, ServletException {
if ( this.dumpRequestParams )
for ( Enumeration paramNames = request.getParameterNames( ); paramNames
.hasMoreElements( ); ) {
String name = ( String ) paramNames.nextElement( );
this.context.log( "Request parameter: " + name + "="
+ request.getParameter( name ) );
}
long startTime = System.currentTimeMillis( );
chain.doFilter( request, response );
this.context.log( "Filter chain executed in "
+ ( System.currentTimeMillis( ) - startTime ) + "ms" );
}
}
1 package winstone.testApplication.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
13 public class WriteAfterServletFilter implements Filter {
15 public void init( FilterConfig filterConfig ) throws ServletException {}
16 public void destroy( ) {}
18 public void doFilter( ServletRequest request, ServletResponse response,
19 FilterChain chain ) throws IOException, ServletException {
chain.doFilter( request, response );
ServletOutputStream os = response.getOutputStream( );
os.print( "Hello" );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testApplication.listeners;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* Logs messages when any session event is received
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: SessionListener.java, v 1.2 2006/02/28 07:32:46 rickknowles Exp $
*/
21 public class SessionListener implements HttpSessionListener,
HttpSessionAttributeListener, HttpSessionActivationListener {
23 public void sessionCreated( HttpSessionEvent se ) {
se.getSession( ).getServletContext( ).log(
"Session Created - id=" + se.getSession( ).getId( ) );
}
28 public void sessionDestroyed( HttpSessionEvent se ) {
se.getSession( ).getServletContext( ).log(
"Session Destroyed - id=" + se.getSession( ).getId( ) );
}
33 public void attributeAdded( HttpSessionBindingEvent se ) {
se.getSession( ).getServletContext( ).log(
"Session Attribute added ( session id="
+ se.getSession( ).getId( ) + " ) " + se.getName( ) + "="
+ se.getValue( ) );
}
40 public void attributeRemoved( HttpSessionBindingEvent se ) {
se.getSession( ).getServletContext( ).log(
"Session Attribute removed ( session id="
+ se.getSession( ).getId( ) + " ) " + se.getName( ) + "="
+ se.getValue( ) );
}
47 public void attributeReplaced( HttpSessionBindingEvent se ) {
se.getSession( ).getServletContext( ).log(
"Session Attribute replaced ( session id="
+ se.getSession( ).getId( ) + " ) " + se.getName( ) + "="
+ se.getValue( ) );
}
54 public void sessionDidActivate( HttpSessionEvent se ) {
se.getSession( ).getServletContext( ).log(
"Session activated - id=" + se.getSession( ).getId( ) );
}
59 public void sessionWillPassivate( HttpSessionEvent se ) {
se.getSession( ).getServletContext( ).log(
"Session passivating - id=" + se.getSession( ).getId( ) );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testApplication.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Simple test servlet that counts the number of times it has been requested,
* and returns that number in the response.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: CountRequestsServlet.java, v 1.3 2006/02/28 07:32:49 rickknowles Exp $
*/
24 public class CountRequestsServlet extends HttpServlet {
private int numberOfGets;
27 public void init( ) {
String offset = getServletConfig( ).getInitParameter( "offset" );
numberOfGets = offset == null ? 0 : Integer.parseInt( offset );
}
/**
* Get implementation - increments and shows the access count
*/
35 protected void doGet( HttpServletRequest request,
36 HttpServletResponse response ) throws ServletException, IOException {
numberOfGets++;
ServletOutputStream out = response.getOutputStream( );
out.println( "<html><body>This servlet has been accessed via GET "
+ numberOfGets + " times</body></html>" );
out.flush( );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testApplication.servlets;
import java.io.IOException;
import java.io.Writer;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Used to test the unavailable exception processing
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: UnavailableServlet.java, v 1.2 2006/02/28 07:32:49 rickknowles Exp $
*/
24 public class UnavailableServlet extends HttpServlet {
protected boolean errorAtInit;
27 public void init( ) throws ServletException {
String errorTime = getServletConfig( ).getInitParameter( "errorTime" );
this.errorAtInit = ( ( errorTime == null ) || errorTime.equals( "init" ) );
if ( this.errorAtInit )
throw new UnavailableException(
"Error thrown deliberately during init" );
}
35 protected void doGet( HttpServletRequest request,
36 HttpServletResponse response ) throws ServletException, IOException {
if ( !this.errorAtInit )
throw new UnavailableException(
"Error thrown deliberately during get" );
Writer out = response.getWriter( );
out
.write( "This should not be shown, because we've thrown unavailable exceptions" );
out.close( );
}
}
1 package winstone.testCase;
import junit.framework.TestCase;
import winstone.auth.BasicAuthenticationHandler;
6 public class Base64Test extends TestCase {
7 public Base64Test( String name ) {
super( name );
}
// The letters a-y encoded in base 64
12 private static String ENCODED_PLUS_ONE = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ==";
13 private static String ENCODED_PLUS_TWO = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=";
15 public void testDecode( ) throws Exception {
String decoded = decodeBase64( ENCODED_PLUS_TWO );
String expected = "abcdefghijklmnopqrstuvwxyz";
assertEquals( "Straight decode failed", expected, decoded );
decoded = decodeBase64( ENCODED_PLUS_ONE );
expected = "abcdefghijklmnopqrstuvwxy";
assertEquals( "Decode failed", expected, decoded );
}
25 public static void testVersusPostgres( ) throws Exception {
String decoded = decodeBase64( "MTIzNDU2Nzg5MA==" );
assertEquals( "Straight encode failed", "1234567890", decoded );
}
/**
* Expects the classic base64 "abcdefgh=" syntax ( equals padded )
* and decodes it to original form
*/
34 public static String decodeBase64( String input ) {
char[] inBytes = input.toCharArray( );
byte[] outBytes = new byte[( int ) ( inBytes.length * 0.75f )]; // always mod 4 = 0
int length = BasicAuthenticationHandler.decodeBase64( inBytes, outBytes, 0, inBytes.length, 0 );
return new String( outBytes, 0, length );
}
41 public static String hexEncode( byte input[] ) {
StringBuffer out = new StringBuffer( );
for ( int i = 0; i < input.length; i++ )
out.append( Integer.toString( ( input[i] & 0xf0 ) >> 4, 16 ) )
.append( Integer.toString( input[i] & 0x0f, 16 ) );
return out.toString( );
}
52 public static byte[] hexDecode( String input ) {
if ( input == null ) {
return null;
} else if ( input.length( ) % 2 != 0 ) {
throw new RuntimeException( "Invalid hex for decoding: " + input );
} else {
byte output[] = new byte[input.length( ) / 2];
for ( int i = 0; i < output.length; i++ ) {
int twoByte = Integer.parseInt( input.substring( i * 2, i * 2 + 2 ), 16 );
output[i] = ( byte ) ( twoByte& 0xff );
}
return output;
}
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testCase;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.xml.sax.SAXException;
import winstone.Launcher;
import winstone.Logger;
import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebImage;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;
/**
* Test case for the Http Connector to Winstone. Simulates a simple connect and
* retrieve case, then a keep-alive connection case.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: HttpConnectorTest.java, v 1.8 2007/04/23 15:06:22 rickknowles Exp $
*/
36 public class HttpConnectorTest extends TestCase {
37 public static Test suite( ) {
return ( new TestSuite( HttpConnectorTest.class ) );
}
/**
* Constructor
*/
44 public HttpConnectorTest( String name ) {
super( name );
}
/**
* Test the simple case of connecting, retrieving and disconnecting
*/
51 public void testSimpleConnection( ) throws IOException, SAXException,
InterruptedException {
// Initialise container
Map args = new HashMap( );
args.put( "webroot", "target/testwebapp" );
args.put( "prefix", "/examples" );
args.put( "httpPort", "10003" );
args.put( "ajp13Port", "-1" );
args.put( "controlPort", "-1" );
args.put( "debug", "8" );
args.put( "logThrowingLineNo", "true" );
Logger.init( Logger.FULL_DEBUG, System.out, true );
Launcher winstone = new Launcher( args );
// Check for a simple connection
WebConversation wc = new WebConversation( );
WebRequest wreq = new GetMethodWebRequest(
"http://localhost:10003/examples/CountRequestsServlet" );
WebResponse wresp = wc.getResponse( wreq );
InputStream content = wresp.getInputStream( );
assertTrue( "Loading CountRequestsServlet", content.available( ) > 0 );
content.close( );
winstone.shutdown( );
Thread.sleep( 500 );
}
/**
* Test the keep alive case
*/
80 public void testKeepAliveConnection( ) throws IOException,
InterruptedException, SAXException {
// Initialise container
Map args = new HashMap( );
args.put( "webroot", "target/testwebapp" );
args.put( "prefix", "/examples" );
args.put( "httpPort", "10004" );
args.put( "ajp13Port", "-1" );
args.put( "controlPort", "-1" );
args.put( "debug", "8" );
args.put( "logThrowingLineNo", "true" );
Logger.init( Logger.FULL_DEBUG, System.out, true );
Launcher winstone = new Launcher( args );
// Check for a simple connection
WebConversation wc = new WebConversation( );
WebRequest wreq = new GetMethodWebRequest(
"http://localhost:10004/examples/CountRequestsServlet" );
WebResponse wresp1 = wc.getResponse( wreq );
WebImage img[] = wresp1.getImages( );
for ( int n = 0; n < img.length; n++ )
wc.getResponse( img[n].getRequest( ) );
// Thread.sleep( 2000 );
// WebResponse wresp2 = wc.getResponse( wreq );
// Thread.sleep( 2000 );
//WebResponse wresp3 = wc.getResponse( wreq );
InputStream content = wresp1.getInputStream( );
assertTrue( "Loading CountRequestsServlet + child images", content
.available( ) > 0 );
content.close( );
winstone.shutdown( );
Thread.sleep( 500 );
}
/**
* Test the keep alive case
*/
117 public void testWriteAfterServlet( ) throws IOException,
InterruptedException, SAXException {
// Initialise container
Map args = new HashMap( );
args.put( "webroot", "target/testwebapp" );
args.put( "prefix", "/examples" );
args.put( "httpPort", "10005" );
args.put( "ajp13Port", "-1" );
args.put( "controlPort", "-1" );
args.put( "debug", "8" );
args.put( "logThrowingLineNo", "true" );
Logger.init( Logger.FULL_DEBUG, System.out, true );
Launcher winstone = new Launcher( args );
// Check for a simple connection
WebConversation wc = new WebConversation( );
WebRequest wreq = new GetMethodWebRequest(
"http://localhost:10005/examples/TestWriteAfterServlet" );
WebResponse wresp1 = wc.getResponse( wreq );
Logger.logDirectMessage( Logger.INFO, "log", "Output: " + wresp1.getText( ), null );
assertTrue( wresp1.getText( ).endsWith( "Hello" ) );
winstone.shutdown( );
Thread.sleep( 500 );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testCase;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Automated tests for the JNDI provider component of Winstone
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: NamingTest.java, v 1.2 2006/02/28 07:32:49 rickknowles Exp $
*/
26 public class NamingTest extends TestCase {
27 public static Test suite( ) {
return ( new TestSuite( NamingTest.class ) );
}
31 private InitialContext ic;
/**
* Constructor for the junit test class for the JNDI service.
*
* @param name
* The name of the test case
*/
39 public NamingTest( String name ) {
super( name );
}
/**
* Begins the setup of the test case
*/
46 public void setUp( ) throws NamingException {
Hashtable env = new Hashtable( );
env.put( Context.INITIAL_CONTEXT_FACTORY,
"winstone.jndi.java.javaURLContextFactory" );
env.put( Context.URL_PKG_PREFIXES, "winstone.jndi" );
this.ic = new InitialContext( env );
}
/**
* Undoes any setup work for the test case
*/
57 public void tearDown( ) throws NamingException {
this.ic.close( );
this.ic = null;
}
/**
* Performs an absolute context lookup
*/
65 public void testAbsoluteContextLookup( ) throws NamingException {
Object context1 = this.ic.lookup( "java:/comp/env" );
assertNotNull( "Lookup on java:/comp/env must be non-null", context1 );
assertTrue( "Lookup on java:/comp/env must be a Context",
context1 instanceof Context );
Object context2 = this.ic.lookup( "java:/comp/env/" );
assertNotNull( "Lookup on java:/comp/env/ must be non-null", context2 );
assertTrue( "Lookup on java:/comp/env/ must be a Context",
context2 instanceof Context );
}
/**
* Performs an absolute lookup on the context
*/
80 public void testAbsoluteLookup( ) throws NamingException {
Object value = this.ic.lookup( "java:/comp/env" );
assertNotNull( "Lookup on java:/comp/env must be non-null", value );
}
/**
* Performs a relative lookup on the context
*/
88 public void testRelativeLookup( ) throws NamingException {
Object value = this.ic.lookup( "" );
assertNotNull( "Lookup on \"\" must be non-null", value );
}
/**
* Performs a relative list on the context
*/
96 public void testRelativeList( ) throws NamingException {
NamingEnumeration listing = this.ic.list( "" );
assertNotNull( "Listing of current context must be non-null", listing );
listing.close( );
}
/**
* Performs an absolute list on the context
*/
105 public void testAbsoluteList( ) throws NamingException {
NamingEnumeration listing1 = this.ic.list( "java:/comp/env" );
assertNotNull( "Listing of java:/comp/env must be non-null", listing1 );
listing1.close( );
NamingEnumeration listing2 = this.ic.list( "java:/comp/env/" );
assertNotNull( "Listing of java:/comp/env/ must be non-null", listing2 );
listing2.close( );
}
/**
* Performs an absolute list on the context
*/
117 public void testCreateDestroyContexts( ) throws NamingException {
Context child = this.ic.createSubcontext( "TestChildContext" );
assertNotNull( "Created subcontext TestChildContext must not be null",
child );
NamingEnumeration listing = child.list( "" );
assertTrue( "Listing on new child context is empty", !listing
.hasMoreElements( ) );
listing.close( );
this.ic.destroySubcontext( "java:/comp/env/TestChildContext" );
}
/**
* Attempts a simple bind
*/
131 public void testSimpleBind( ) throws NamingException {
Context child = this.ic.createSubcontext( "TestBindContext" );
assertNotNull( "Created subcontext TestBindContext must not be null",
child );
child.bind( "bindInteger", new Integer( 80 ) );
Object lookupInt = this.ic.lookup( "TestBindContext/bindInteger" );
assertNotNull(
"java:/comp/env/TestBindContext/bindInteger should be non-null",
lookupInt );
assertEquals( "java:/comp/env/TestBindContext/bindInteger", lookupInt,
new Integer( 80 ) );
this.ic.destroySubcontext( "java:/comp/env/TestBindContext" );
}
/**
* Attempts a rebind
*/
148 public void testSimpleRebind( ) throws NamingException {
Context child = this.ic.createSubcontext( "TestRebindContext" );
assertNotNull( "Created subcontext TestRebindContext must not be null",
child );
Context rebindChild = child.createSubcontext( "ChildRebind" );
assertNotNull( "Created subcontext rebindChild must not be null",
rebindChild );
rebindChild.rebind(
"java:/comp/env/TestRebindContext/ChildRebind/integer",
new Integer( 25 ) );
rebindChild.close( );
child.close( );
Object lookupInt = this.ic
.lookup( "java:/comp/env/TestRebindContext/ChildRebind/integer" );
assertNotNull(
"java:/comp/env/TestRebindContext/ChildRebind/integer should be non-null",
lookupInt );
assertEquals( "java:/comp/env/TestRebindContext/ChildRebind/integer",
lookupInt, new Integer( 25 ) );
this.ic
.rebind( "TestRebindContext/ChildRebind/integer",
new Integer( 40 ) );
Object lookupInt2 = this.ic
.lookup( "TestRebindContext/ChildRebind/integer" );
assertNotNull(
"TestRebindContext/ChildRebind/integer should be non-null",
lookupInt2 );
assertEquals( "TestRebindContext/ChildRebind/integer", lookupInt2,
new Integer( 40 ) );
Object lookupInt3 = this.ic
.lookup( "java:/comp/env/TestRebindContext/ChildRebind/integer" );
assertNotNull(
"java:/comp/env/TestRebindContext/ChildRebind/integer should be non-null",
lookupInt3 );
assertEquals( "java:/comp/env/TestRebindContext/ChildRebind/integer",
lookupInt3, new Integer( 40 ) );
this.ic
.destroySubcontext( "java:/comp/env/TestRebindContext/ChildRebind" );
this.ic.destroySubcontext( "java:/comp/env/TestRebindContext" );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testCase;
import java.io.File;
import java.io.IOException;
import junit.framework.TestCase;
import winstone.StaticResourceServlet;
/**
* Automated tests for the url security check inside the static resource servlet
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: StaticResourceServletTest.java, v 1.2 2006/02/28 07:32:49 rickknowles Exp $
*/
21 public class StaticResourceServletTest extends TestCase {
/**
* Constructor
*/
26 public StaticResourceServletTest( String name ) {
super( name );
}
30 public void testIsDescendant( ) throws IOException {
File webroot = new File( "src/testwebapp" );
File webinf = new File( webroot, "WEB-INF" );
assertTrue( "Direct subfolder", StaticResourceServlet.isDescendant( webroot, webinf, webroot ) );
assertTrue( "Self is a descendent of itself", StaticResourceServlet.isDescendant( webinf, webinf, webroot ) );
assertTrue( "Direct subfile", StaticResourceServlet.isDescendant( webinf, new File( webinf, "web.xml" ), webroot ) );
assertTrue( "Indirect subfile", StaticResourceServlet.isDescendant( webroot, new File( webinf, "web.xml" ), webroot ) );
assertTrue( "Backwards iterations", !StaticResourceServlet.isDescendant( webinf, new File( webinf, ".." ), webroot ) );
}
40 public void testCanonicalVersion( ) throws IOException {
File webroot = new File( "src/testwebapp" );
File webinf = new File( webroot, "WEB-INF" );
File webxml = new File( webinf, "web.xml" );
assertTrue( "Simplest case",
StaticResourceServlet.constructOurCanonicalVersion(
webxml, webroot ).equals( "/WEB-INF/web.xml" ) );
assertTrue( "One back step",
StaticResourceServlet.constructOurCanonicalVersion(
new File( webroot, "/test/../WEB-INF/web.xml" ), webroot )
.equals( "/WEB-INF/web.xml" ) );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testCase;
import junit.framework.TestCase;
import winstone.WinstoneResourceBundle;
/**
* Simple tests for the string replacer
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneResourceBundleTest.java, v 1.1 2006/11/09 05:39:43 rickknowles Exp $
*/
18 public class WinstoneResourceBundleTest extends TestCase {
20 public static void testGlobalReplace( ) throws Exception {
assertEquals( "One token", "Foo = bar squared", WinstoneResourceBundle.globalReplace(
"Foo = [#0] squared", "[#0]", "bar" ) );
assertEquals( "Repeated token", "Foo = bar bar squared", WinstoneResourceBundle.globalReplace(
"Foo = [#0] [#0] squared", "[#0]", "bar" ) );
assertEquals( "Two tokens", "Foo = blah bar squared", WinstoneResourceBundle.globalReplace(
"Foo = [#1] [#0] squared", new String[][] {{"[#0]", "bar"}, {"[#1]", "blah"}} ) );
}
// public static void testSpeed( ) throws Exception {
// String tokens[][] = new String[20][2];
// for ( int n = 0; n < tokens.length; n++ ) {
// tokens[n] = new String[] {"[#" + n + "]", "token" + n};
// }
// Random rnd = new Random( );
// String inputs[] = new String[5000];
// for ( int n = 0; n < inputs.length; n++ ) {
// inputs[n] = "";
// for ( int k = 0; k < tokens.length; k++ ) {
// inputs[n] += "[#" + ( rnd.nextInt( ) % tokens.length ) + "] abc";
// }
// }
//
// long startTime = System.currentTimeMillis( );
// for ( int n = 0; n < inputs.length; n++ ) {
// WinstoneResourceBundle.globalReplace( inputs[n], tokens );
// }
// System.out.println( "Replaced " + tokens.length + " tokens in " + inputs.length + " strings in " +
// ( System.currentTimeMillis( ) - startTime ) + "ms" );
// }
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testCase.load;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import winstone.Logger;
import winstone.WebAppConfiguration;
import winstone.WinstoneResourceBundle;
import com.meterware.httpunit.WebConversation;
/**
* This class is an attempt to benchmark performance under load for winstone. It
* works by hitting a supplied URL with parallel threads ( with keep-alives or
* without ) at an escalating rate, and counting the no of failures.
*
* It uses HttpUnit's WebConversation class for the connection.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: LoadTest.java, v 1.2 2006/02/28 07:32:49 rickknowles Exp $
*/
32 public class LoadTest {
33 private String url;
private boolean useKeepAlives;
private int startThreads;
private int endThreads;
private int stepSize;
private long stepPeriod;
private long gracePeriod;
private long successTimeTotal;
private int successCount;
42 private WinstoneResourceBundle resources;
44 private static String LOCAL_RESOURCE_FILE = "winstone.testCase.load.LocalStrings";
46 public LoadTest( WinstoneResourceBundle resources, String url,
boolean useKeepAlives, int startThreads, int endThreads,
int stepSize, long stepPeriod, long gracePeriod ) {
this.resources = resources;
this.url = url;
this.useKeepAlives = useKeepAlives;
this.startThreads = startThreads;
this.endThreads = endThreads;
this.stepSize = stepSize;
this.stepPeriod = stepPeriod;
this.gracePeriod = gracePeriod;
Logger.log( Logger.INFO, resources, "LoadTest.Config", new String[] {
this.url, this.useKeepAlives + "", this.startThreads + "",
this.endThreads + "", this.stepSize + "", this.stepPeriod + "",
this.gracePeriod + "" } );
}
64 public void test( ) throws InterruptedException {
WebConversation wc = null;
// Loop through in steps
for ( int n = this.startThreads; n <= this.endThreads; n += this.stepSize ) {
if ( this.useKeepAlives )
wc = new WebConversation( );
// Spawn the threads
int noOfSeconds = ( int ) this.stepPeriod / 1000;
List threads = new ArrayList( );
for ( int m = 0; m < n; m++ )
threads.add( new LoadTestThread( this.url, this, this.resources,
wc, noOfSeconds - 1 ) );
// Sleep for step period
Thread.sleep( this.stepPeriod + gracePeriod );
// int errorCount = ( noOfSeconds * n ) - this.successCount;
Long averageSuccessTime = this.successCount == 0 ? null : new Long(
this.successTimeTotal / this.successCount );
// Write out results
Logger.log( Logger.INFO, resources, "LoadTest.LineResult",
new String[] { n + "", this.successCount + "",
( ( noOfSeconds * n ) - this.successCount ) + "",
averageSuccessTime + "" } );
// Close threads
for ( Iterator i = threads.iterator( ); i.hasNext( ); )
( ( LoadTestThread ) i.next( ) ).destroy( );
this.successTimeTotal = 0;
this.successCount = 0;
}
}
102 public void incTimeTotal( long amount ) {
this.successTimeTotal += amount;
}
106 public void incSuccessCount( ) {
this.successCount++;
}
110 public static void main( String args[] ) throws Exception {
WinstoneResourceBundle resources = new WinstoneResourceBundle(
LOCAL_RESOURCE_FILE );
// Loop for args
Map options = new HashMap( );
// String operation = "";
for ( int n = 0; n < args.length; n++ ) {
String option = args[n];
if ( option.startsWith( "--" ) ) {
int equalPos = option.indexOf( '=' );
String paramName = option.substring( 2, equalPos == -1 ? option
.length( ) : equalPos );
String paramValue = ( equalPos == -1 ? "true" : option
.substring( equalPos + 1 ) );
options.put( paramName, paramValue );
}
}
if ( options.size( ) == 0 ) {
printUsage( resources );
return;
}
Logger.setCurrentDebugLevel( Integer.parseInt( WebAppConfiguration
.stringArg( options, "debug", "5" ) ) );
String url = WebAppConfiguration.stringArg( options, "url",
"http://localhost:8080/" );
boolean keepAlive = WebAppConfiguration.booleanArg( options,
"keepAlive", true );
String startThreads = WebAppConfiguration.stringArg( options,
"startThreads", "20" );
String endThreads = WebAppConfiguration.stringArg( options,
"endThreads", "1000" );
String stepSize = WebAppConfiguration.stringArg( options, "stepSize",
"20" );
String stepPeriod = WebAppConfiguration.stringArg( options,
"stepPeriod", "5000" );
String gracePeriod = WebAppConfiguration.stringArg( options,
"gracePeriod", "5000" );
LoadTest lt = new LoadTest( resources, url, keepAlive, Integer
.parseInt( startThreads ), Integer.parseInt( endThreads ), Integer
.parseInt( stepSize ), Integer.parseInt( stepPeriod ), Integer
.parseInt( gracePeriod ) );
lt.test( );
}
/**
* Displays the usage message
*/
162 private static void printUsage( WinstoneResourceBundle resources )
throws IOException {
System.out.println( resources.getString( "LoadTest.Usage" ) );
}
}
1 /*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license ( CDDL ), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.testCase.load;
import java.io.IOException;
import java.io.InputStream;
import org.xml.sax.SAXException;
import winstone.Logger;
import winstone.WinstoneResourceBundle;
import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;
/**
* A single worked thread in the load testing program
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: LoadTestThread.java, v 1.2 2006/02/28 07:32:49 rickknowles Exp $
*/
28 public class LoadTestThread implements Runnable {
29 private WinstoneResourceBundle resources;
30 private String url;
private long delayBeforeStarting;
32 private LoadTest loadTest;
33 private WebConversation webConv;
34 private Thread thread;
private boolean interrupted;
private LoadTestThread next;
38 public LoadTestThread( String url, LoadTest loadTest,
39 WinstoneResourceBundle resources, WebConversation webConv,
int delayedThreads ) {
this.resources = resources;
this.url = url;
this.loadTest = loadTest;
this.webConv = webConv;
this.delayBeforeStarting = 1000 * delayedThreads;
this.interrupted = false;
this.thread = new Thread( this );
this.thread.setDaemon( true );
this.thread.start( );
// Launch the next second's getter
if ( delayedThreads > 0 )
this.next = new LoadTestThread( url, loadTest, resources, webConv,
delayedThreads - 1 );
}
57 public void run( ) {
if ( this.delayBeforeStarting > 0 )
try {
Thread.sleep( this.delayBeforeStarting );
} catch ( InterruptedException err ) {
}
long startTime = System.currentTimeMillis( );
try {
if ( this.webConv == null )
this.webConv = new WebConversation( );
// Access the URL
WebRequest wreq = new GetMethodWebRequest( this.url );
WebResponse wresp = this.webConv.getResponse( wreq );
int responseCode = wresp.getResponseCode( );
if ( responseCode >= 400 )
throw new IOException( "Failed with status " + responseCode );
InputStream inContent = wresp.getInputStream( );
int contentLength = wresp.getContentLength( );
byte content[] = new byte[contentLength == -1 ? 100 * 1024
: contentLength];
int position = 0;
int value = inContent.read( );
while ( ( value != -1 )
&& ( ( ( contentLength >= 0 ) && ( position < contentLength ) ) || ( contentLength < 0 ) ) ) {
content[position++] = ( byte ) value;
value = inContent.read( );
}
inContent.close( );
// Confirm the result is the same size the content-length said it
// was
if ( ( position == contentLength ) || ( contentLength == -1 ) ) {
if ( this.interrupted )
return;
this.loadTest.incTimeTotal( System.currentTimeMillis( )
- startTime );
this.loadTest.incSuccessCount( );
} else
throw new IOException( "Only downloaded " + position + " of "
+ contentLength + " bytes" );
} catch ( IOException err ) {
Logger.log( Logger.DEBUG, resources, "LoadTestThread.Error", err );
} catch ( SAXException err ) {
Logger.log( Logger.DEBUG, resources, "LoadTestThread.Error", err );
}
}
107 public void destroy( ) {
this.interrupted = true;
this.thread.interrupt( );
if ( this.next != null )
this.next.destroy( );
}
}