/*=============================================================================*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*=============================================================================*/
package org.apache.ws.util.test.axis;
import org.apache.axis.EngineConfiguration;
import org.apache.axis.collections.LRUMap;
import org.apache.axis.components.logger.LogFactory;
import org.apache.axis.components.threadpool.ThreadPool;
import org.apache.axis.configuration.EngineConfigurationFactoryFinder;
import org.apache.axis.management.ServiceAdmin;
import org.apache.axis.server.AxisServer;
import org.apache.axis.session.Session;
import org.apache.axis.session.SimpleSession;
import org.apache.axis.transport.http.SimpleAxisServer;
import org.apache.axis.transport.http.SimpleAxisWorker;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.NetworkUtils;
import org.apache.axis.utils.Options;
import org.apache.commons.logging.Log;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
/**
* This is a simple implementation of an HTTP server for processing SOAP requests via Apache's xml-axis. This is not
* intended for production use. Its intended uses are for demos, debugging, and performance profiling.
* <p/>
* Note this classes uses static objects to provide a thread pool, so you should not use multiple instances of this
* class in the same JVM/classloader unless you want bad things to happen at shutdown.
*
* TODO: delete any methods that can be safely inherited from superclass
*
* @author Ian Springer
*/
public class NotSoSimpleAxisServer
extends SimpleAxisServer
implements Runnable
{
/** DOCUMENT_ME */
public static final File DEFAULT_DOC_ROOT_DIR = new File( "./src/wsdl" );
/** DOCUMENT_ME */
public static final int DEFAULT_MAX_THREADS = 200;
/** DOCUMENT_ME */
public static final int DEFAULT_MAX_SESSIONS = 100;
/** DOCUMENT_ME */
protected static final Log LOG = LogFactory.getLog( NotSoSimpleAxisServer.class.getName( ) );
/*
* pool of threads
*/
private static ThreadPool m_pool;
/*
* Are we doing threads?
*/
private static boolean s_doThreads = true;
/*
* Are we doing sessions? Set this to false if you don't want any session overhead.
*/
private static boolean s_doSessions = true;
// What is our current session index?
// This is a monotonically increasing, non-thread-safe integer
// (thread safety not considered crucial here)
/** DOCUMENT_ME */
public static int sessionIndex = 0;
// Axis server (shared between instances)
private static AxisServer myAxisServer = null;
// session state.
// This table maps session keys (random numbers) to SimpleAxisSession objects.
//
// There is a simple LRU based session cleanup mechanism, but if clients are not
// passing cookies, then a new session will be created for *every* request.
private Map m_sessions;
//Maximum capacity of the LRU Map used for session cleanup
private int m_maxSessions;
private File m_docRootDir;
private EngineConfiguration m_myConfig = null;
/**
* are we stopped? latch to true if stop() is called
*/
private boolean stopped = false;
// per thread socket information
private ServerSocket m_serverSocket;
/**
* Create a server with default options.
*/
public NotSoSimpleAxisServer( )
{
this( DEFAULT_DOC_ROOT_DIR );
}
/**
* Create a server with the specified docRoot.
*/
public NotSoSimpleAxisServer( File docRootDir )
{
this( docRootDir, DEFAULT_MAX_THREADS );
}
/**
* Create a server with the specified docRoot and max threads.
*/
public NotSoSimpleAxisServer( File docRootDir,
int maxPoolSize )
{
this( docRootDir, maxPoolSize, DEFAULT_MAX_SESSIONS );
}
/**
* Create a server with the specified docRoot, max threads, and max sessions.
*/
public NotSoSimpleAxisServer( File docRootDir,
int maxPoolSize,
int maxSessions )
{
m_docRootDir = docRootDir;
m_pool = new ThreadPool( maxPoolSize );
m_sessions = new LRUMap( maxSessions );
}
/**
* get the thread pool
*
* @return
*/
public static ThreadPool getPool( )
{
return m_pool;
}
/**
* demand create an axis server; return an existing one if one exists. The configuration for the axis server is
* derived from #myConfig if not null, the default config otherwise.
*
* @return
*/
public synchronized AxisServer getAxisServer( )
{
if ( myAxisServer == null )
{
if ( m_myConfig == null )
{
m_myConfig = EngineConfigurationFactoryFinder.newFactory( ).getServerEngineConfig( );
}
myAxisServer = new AxisServer( m_myConfig );
ServiceAdmin.setEngine( myAxisServer,
NetworkUtils.getLocalHostname( ) + "@" + m_serverSocket.getLocalPort( ) );
}
return myAxisServer;
}
/**
* turn threading on or off. This sets a static value
*
* @param value
*/
public void setDoThreads( boolean value )
{
s_doThreads = value;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public boolean getDoThreads( )
{
return s_doThreads;
}
/**
* Resize the session map
*
* @param maxSessions maximum sessions
*/
public void setMaxSessions( int maxSessions )
{
this.m_maxSessions = maxSessions;
( (LRUMap) m_sessions ).setMaximumSize( maxSessions );
}
/**
* get max session count
*
* @return
*/
public int getMaxSessions( )
{
return m_maxSessions;
}
/**
* DOCUMENT_ME
*
* @param myConfig DOCUMENT_ME
*/
public void setMyConfig( EngineConfiguration myConfig )
{
m_myConfig = myConfig;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public EngineConfiguration getMyConfig( )
{
return m_myConfig;
}
/**
* Set the serverSocket this server should listen on. (note : changing this will not affect a running server, but if
* you stop() and then start() the server, the new socket will be used).
*/
public void setServerSocket( ServerSocket serverSocket )
{
m_serverSocket = serverSocket;
}
/**
* Obtain the serverSocket that that SimpleAxisServer is listening on.
*/
public ServerSocket getServerSocket( )
{
return m_serverSocket;
}
/**
* Server process.
*/
public static void main( String[] args )
{
Options opts = null;
try
{
opts = new Options( args );
}
catch ( MalformedURLException e )
{
LOG.error( Messages.getMessage( "malformedURLException00" ),
e );
return;
}
String maxPoolSize = opts.isValueSet( 't' );
if ( maxPoolSize == null )
{
maxPoolSize = ThreadPool.DEFAULT_MAX_THREADS + "";
}
String maxSessions = opts.isValueSet( 'm' );
if ( maxSessions == null )
{
maxSessions = DEFAULT_MAX_SESSIONS + "";
}
String[] nonOptionArgs = opts.getRemainingArgs( );
NotSoSimpleAxisServer server =
new NotSoSimpleAxisServer( new File( nonOptionArgs[0] ),
Integer.parseInt( maxPoolSize ),
Integer.parseInt( maxSessions ) );
try
{
s_doThreads = ( opts.isFlagSet( 't' ) > 0 );
int port = opts.getPort( );
ServerSocket ss = null;
// Try five times
final int retries = 5;
for ( int i = 0; i < retries; i++ )
{
try
{
ss = new ServerSocket( port );
break;
}
catch ( java.net.BindException be )
{
LOG.debug( Messages.getMessage( "exception00" ),
be );
if ( i < ( retries - 1 ) )
{
// At 3 second intervals.
Thread.sleep( 3000 );
}
else
{
throw new Exception( Messages.getMessage( "unableToStartServer00",
Integer.toString( port ) ) );
}
}
}
server.setServerSocket( ss );
server.start( );
}
catch ( Exception e )
{
LOG.error( Messages.getMessage( "exception00" ),
e );
return;
}
}
/**
* Accept requests from a given TCP port and send them through the Axis engine for processing.
*/
public void run( )
{
LOG.info( Messages.getMessage( "start01",
"SimpleAxisServer",
new Integer( getServerSocket( ).getLocalPort( ) ).toString( ),
getCurrentDirectory( ) ) );
// Accept and process requests from the socket
while ( !stopped )
{
Socket socket = null;
try
{
socket = m_serverSocket.accept( );
}
catch ( java.io.InterruptedIOException iie )
{
}
catch ( Exception e )
{
LOG.debug( Messages.getMessage( "exception00" ),
e );
break;
}
if ( socket != null )
{
SimpleAxisWorker worker = new NotSoSimpleAxisWorker( this, socket, m_docRootDir );
if ( s_doThreads )
{
m_pool.addWorker( worker );
}
else
{
worker.run( );
}
}
}
LOG.info( Messages.getMessage( "quit00", "SimpleAxisServer" ) );
}
/**
* Start this server.
* <p/>
* Spawns a worker thread to listen for HTTP requests.
*
* @param daemon a boolean indicating if the thread should be a daemon.
*/
public void start( boolean daemon )
throws Exception
{
stopped = false;
if ( s_doThreads )
{
Thread thread = new Thread( this );
thread.setDaemon( daemon );
thread.start( );
}
else
{
run( );
}
}
/**
* Start this server as a NON-daemon.
*/
public void start( )
throws Exception
{
start( false );
}
/**
* Stop this server. Can be called safely if the system is already stopped, or if it was never started.
* <p/>
* This will interrupt any pending accept().
*/
public void stop( )
{
//recognise use before we are live
if ( stopped )
{
return;
}
/*
* Close the server socket cleanly, but avoid fresh accepts while
* the socket is closing.
*/
stopped = true;
try
{
if ( m_serverSocket != null )
{
m_serverSocket.close( );
}
}
catch ( IOException e )
{
LOG.info( Messages.getMessage( "exception00" ),
e );
}
finally
{
m_serverSocket = null;
}
LOG.info( Messages.getMessage( "quit00", "SimpleAxisServer" ) );
//shut down the pool
m_pool.shutdown( );
}
//---------------------------------------------------
protected boolean isSessionUsed( )
{
return s_doSessions;
}
/**
* demand create a session if there is not already one for the string
*
* @param cooky
*
* @return a session.
*/
protected Session createSession( String cooky )
{
// is there a session already?
Session session = null;
if ( m_sessions.containsKey( cooky ) )
{
session = (Session) m_sessions.get( cooky );
}
else
{
// no session for this cooky, bummer
session = new SimpleSession( );
// ADD CLEANUP LOGIC HERE if needed
m_sessions.put( cooky, session );
}
return session;
}
/**
* stop the server if not already told to.
*
* @throws Throwable
*/
protected void finalize( )
throws Throwable
{
stop( );
super.finalize( );
}
/**
* Gets the current directory
*
* @return current directory
*/
private String getCurrentDirectory( )
{
return System.getProperty( "user.dir" );
}
}