package org.codehaus.mojo.tomcat;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.commons.codec.binary.Base64;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
/**
* A Tomcat manager webapp invocation wrapper.
*
* @author Mark Hobson <markhobson@gmail.com>
* @version $Id$
*/
public class TomcatManager
{
// ----------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------
/**
* The charset to use when decoding Tomcat manager responses.
*/
private static final String MANAGER_CHARSET = "UTF-8";
// ----------------------------------------------------------------------
// Fields
// ----------------------------------------------------------------------
/**
* The full URL of the Tomcat manager instance to use.
*/
private URL url;
/**
* The username to use when authenticating with Tomcat manager.
*/
private String username;
/**
* The password to use when authenticating with Tomcat manager.
*/
private String password;
/**
* The URL encoding charset to use when communicating with Tomcat manager.
*/
private String charset;
/**
* The user agent name to use when communicating with Tomcat manager.
*/
private String userAgent;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
/**
* Creates a Tomcat manager wrapper for the specified URL that uses a username of <code>admin</code>, an empty
* password and ISO-8859-1 URL encoding.
*
* @param url the full URL of the Tomcat manager instance to use
*/
public TomcatManager( URL url )
{
this( url, "admin" );
}
/**
* Creates a Tomcat manager wrapper for the specified URL and username that uses an empty password and ISO-8859-1
* URL encoding.
*
* @param url the full URL of the Tomcat manager instance to use
* @param username the username to use when authenticating with Tomcat manager
*/
public TomcatManager( URL url, String username )
{
this( url, username, "" );
}
/**
* Creates a Tomcat manager wrapper for the specified URL, username and password that uses ISO-8859-1 URL encoding.
*
* @param url the full URL of the Tomcat manager instance to use
* @param username the username to use when authenticating with Tomcat manager
* @param password the password to use when authenticating with Tomcat manager
*/
public TomcatManager( URL url, String username, String password )
{
this( url, username, password, "ISO-8859-1" );
}
/**
* Creates a Tomcat manager wrapper for the specified URL, username, password and URL encoding.
*
* @param url the full URL of the Tomcat manager instance to use
* @param username the username to use when authenticating with Tomcat manager
* @param password the password to use when authenticating with Tomcat manager
* @param charset the URL encoding charset to use when communicating with Tomcat manager
*/
public TomcatManager( URL url, String username, String password, String charset )
{
this.url = url;
this.username = username;
this.password = password;
this.charset = charset;
}
// ----------------------------------------------------------------------
// Public Methods
// ----------------------------------------------------------------------
/**
* Gets the full URL of the Tomcat manager instance.
*
* @return the full URL of the Tomcat manager instance
*/
public URL getURL()
{
return url;
}
/**
* Gets the username to use when authenticating with Tomcat manager.
*
* @return the username to use when authenticating with Tomcat manager
*/
public String getUserName()
{
return username;
}
/**
* Gets the password to use when authenticating with Tomcat manager.
*
* @return the password to use when authenticating with Tomcat manager
*/
public String getPassword()
{
return password;
}
/**
* Gets the URL encoding charset to use when communicating with Tomcat manager.
*
* @return the URL encoding charset to use when communicating with Tomcat manager
*/
public String getCharset()
{
return charset;
}
/**
* Gets the user agent name to use when communicating with Tomcat manager.
*
* @return the user agent name to use when communicating with Tomcat manager
*/
public String getUserAgent()
{
return userAgent;
}
/**
* Sets the user agent name to use when communicating with Tomcat manager.
*
* @param userAgent the user agent name to use when communicating with Tomcat manager
*/
public void setUserAgent( String userAgent )
{
this.userAgent = userAgent;
}
/**
* Deploys the specified WAR as a URL to the specified context path.
*
* @param path the webapp context path to deploy to
* @param war the URL of the WAR to deploy
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deploy( String path, URL war )
throws TomcatManagerException, IOException
{
return deploy( path, war, false );
}
/**
* Deploys the specified WAR as a URL to the specified context path, optionally undeploying the webapp if it already
* exists.
*
* @param path the webapp context path to deploy to
* @param war the URL of the WAR to deploy
* @param update whether to first undeploy the webapp if it already exists
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deploy( String path, URL war, boolean update )
throws TomcatManagerException, IOException
{
return deploy( path, war, update, null );
}
/**
* Deploys the specified WAR as a URL to the specified context path, optionally undeploying the webapp if it already
* exists and using the specified tag name.
*
* @param path the webapp context path to deploy to
* @param war the URL of the WAR to deploy
* @param update whether to first undeploy the webapp if it already exists
* @param tag the tag name to use
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deploy( String path, URL war, boolean update, String tag )
throws TomcatManagerException, IOException
{
return deployImpl( path, null, war, null, update, tag );
}
/**
* Deploys the specified WAR as a HTTP PUT to the specified context path.
*
* @param path the webapp context path to deploy to
* @param war an input stream to the WAR to deploy
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deploy( String path, InputStream war )
throws TomcatManagerException, IOException
{
return deploy( path, war, false );
}
/**
* Deploys the specified WAR as a HTTP PUT to the specified context path, optionally undeploying the webapp if it
* already exists.
*
* @param path the webapp context path to deploy to
* @param war an input stream to the WAR to deploy
* @param update whether to first undeploy the webapp if it already exists
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deploy( String path, InputStream war, boolean update )
throws TomcatManagerException, IOException
{
return deploy( path, war, update, null );
}
/**
* Deploys the specified WAR as a HTTP PUT to the specified context path, optionally undeploying the webapp if it
* already exists and using the specified tag name.
*
* @param path the webapp context path to deploy to
* @param war an input stream to the WAR to deploy
* @param update whether to first undeploy the webapp if it already exists
* @param tag the tag name to use
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deploy( String path, InputStream war, boolean update, String tag )
throws TomcatManagerException, IOException
{
return deployImpl( path, null, null, war, update, tag );
}
/**
* Deploys the specified context XML configuration to the specified context path.
*
* @param path the webapp context path to deploy to
* @param config the URL of the context XML configuration to deploy
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deployContext( String path, URL config )
throws TomcatManagerException, IOException
{
return deployContext( path, config, false );
}
/**
* Deploys the specified context XML configuration to the specified context path, optionally undeploying the webapp
* if it already exists.
*
* @param path the webapp context path to deploy to
* @param config the URL of the context XML configuration to deploy
* @param update whether to first undeploy the webapp if it already exists
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deployContext( String path, URL config, boolean update )
throws TomcatManagerException, IOException
{
return deployContext( path, config, update, null );
}
/**
* Deploys the specified context XML configuration to the specified context path, optionally undeploying the webapp
* if it already exists and using the specified tag name.
*
* @param path the webapp context path to deploy to
* @param config the URL of the context XML configuration to deploy
* @param update whether to first undeploy the webapp if it already exists
* @param tag the tag name to use
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deployContext( String path, URL config, boolean update, String tag )
throws TomcatManagerException, IOException
{
return deployContext( path, config, null, update, tag );
}
/**
* Deploys the specified context XML configuration and WAR as a URL to the specified context path.
*
* @param path the webapp context path to deploy to
* @param config the URL of the context XML configuration to deploy
* @param war the URL of the WAR to deploy
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deployContext( String path, URL config, URL war )
throws TomcatManagerException, IOException
{
return deployContext( path, config, war, false );
}
/**
* Deploys the specified context XML configuration and WAR as a URL to the specified context path, optionally
* undeploying the webapp if it already exists.
*
* @param path the webapp context path to deploy to
* @param config the URL of the context XML configuration to deploy
* @param war the URL of the WAR to deploy
* @param update whether to first undeploy the webapp if it already exists
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deployContext( String path, URL config, URL war, boolean update )
throws TomcatManagerException, IOException
{
return deployContext( path, config, war, update, null );
}
/**
* Deploys the specified context XML configuration and WAR as a URL to the specified context path, optionally
* undeploying the webapp if it already exists and using the specified tag name.
*
* @param path the webapp context path to deploy to
* @param config the URL of the context XML configuration to deploy
* @param war the URL of the WAR to deploy
* @param update whether to first undeploy the webapp if it already exists
* @param tag the tag name to use
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String deployContext( String path, URL config, URL war, boolean update, String tag )
throws TomcatManagerException, IOException
{
return deployImpl( path, config, war, null, update, tag );
}
/**
* Undeploys the webapp at the specified context path.
*
* @param path the webapp context path to undeploy
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String undeploy( String path )
throws TomcatManagerException, IOException
{
return invoke( "/undeploy?path=" + URLEncoder.encode( path, charset ) );
}
/**
* Reloads the webapp at the specified context path.
*
* @param path the webapp context path to reload
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String reload( String path )
throws TomcatManagerException, IOException
{
return invoke( "/reload?path=" + URLEncoder.encode( path, charset ) );
}
/**
* Starts the webapp at the specified context path.
*
* @param path the webapp context path to start
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String start( String path )
throws TomcatManagerException, IOException
{
return invoke( "/start?path=" + URLEncoder.encode( path, charset ) );
}
/**
* Stops the webapp at the specified context path.
*
* @param path the webapp context path to stop
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String stop( String path )
throws TomcatManagerException, IOException
{
return invoke( "/stop?path=" + URLEncoder.encode( path, charset ) );
}
/**
* Lists all the currently deployed web applications.
*
* @return the list of currently deployed applications
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String list()
throws TomcatManagerException, IOException
{
return invoke( "/list" );
}
/**
* Lists information about the Tomcat version, OS, and JVM properties.
*
* @return the server information
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String getServerInfo()
throws TomcatManagerException, IOException
{
return invoke( "/serverinfo" );
}
/**
* Lists all of the global JNDI resources.
*
* @return the list of all global JNDI resources
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String getResources()
throws TomcatManagerException, IOException
{
return getResources( null );
}
/**
* Lists the global JNDI resources of the given type.
*
* @param type the class name of the resources to list, or <code>null</code> for all
* @return the list of global JNDI resources of the given type
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String getResources( String type )
throws TomcatManagerException, IOException
{
StringBuffer buffer = new StringBuffer();
buffer.append( "/resources" );
if ( type != null )
{
buffer.append( "?type=" + URLEncoder.encode( type, charset ) );
}
return invoke( buffer.toString() );
}
/**
* Lists the security role names and corresponding descriptions that are available.
*
* @return the list of security role names and corresponding descriptions
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String getRoles()
throws TomcatManagerException, IOException
{
return invoke( "/roles" );
}
/**
* Lists the default session timeout and the number of currently active sessions for the given context path.
*
* @param path the context path to list session information for
* @return the default session timeout and the number of currently active sessions
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
public String getSessions( String path )
throws TomcatManagerException, IOException
{
return invoke( "/sessions?path=" + URLEncoder.encode( path, charset ) );
}
// ----------------------------------------------------------------------
// Protected Methods
// ----------------------------------------------------------------------
/**
* Invokes Tomcat manager with the specified command.
*
* @param path the Tomcat manager command to invoke
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
protected String invoke( String path )
throws TomcatManagerException, IOException
{
return invoke( path, null );
}
/**
* Invokes Tomcat manager with the specified command and content data.
*
* @param path the Tomcat manager command to invoke
* @param data an input stream to the content data
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
protected String invoke( String path, InputStream data )
throws TomcatManagerException, IOException
{
HttpURLConnection connection = (HttpURLConnection) new URL( url + path ).openConnection();
connection.setAllowUserInteraction( false );
connection.setDoInput( true );
connection.setUseCaches( false );
if ( data == null )
{
connection.setDoOutput( false );
connection.setRequestMethod( "GET" );
}
else
{
connection.setDoOutput( true );
connection.setRequestMethod( "PUT" );
connection.setRequestProperty( "Content-Type", "application/octet-stream" );
}
if ( userAgent != null )
{
connection.setRequestProperty( "User-Agent", userAgent );
}
connection.setRequestProperty( "Authorization", toAuthorization( username, password ) );
connection.connect();
if ( data != null )
{
pipe( data, connection.getOutputStream() );
}
String response = toString( connection.getInputStream(), MANAGER_CHARSET );
if ( !response.startsWith( "OK -" ) )
{
throw new TomcatManagerException( response );
}
return response;
}
// ----------------------------------------------------------------------
// Private Methods
// ----------------------------------------------------------------------
/**
* Deploys the specified WAR.
*
* @param path the webapp context path to deploy to
* @param config the URL of the context XML configuration to deploy, or null for none
* @param war the URL of the WAR to deploy, or null to use <code>data</code>
* @param data an input stream to the WAR to deploy, or null to use <code>war</code>
* @param update whether to first undeploy the webapp if it already exists
* @param tag the tag name to use
* @return the Tomcat manager response
* @throws TomcatManagerException if the Tomcat manager request fails
* @throws IOException if an i/o error occurs
*/
private String deployImpl( String path, URL config, URL war, InputStream data, boolean update, String tag )
throws TomcatManagerException, IOException
{
StringBuffer buffer = new StringBuffer( "/deploy" );
buffer.append( "?path=" ).append( URLEncoder.encode( path, charset ) );
if ( config != null )
{
buffer.append( "&config=" ).append( URLEncoder.encode( config.toString(), charset ) );
}
if ( war != null )
{
buffer.append( "&war=" ).append( URLEncoder.encode( war.toString(), charset ) );
}
else
{
// for Tomcat 5.0.27
buffer.append( "&war=" );
}
if ( update )
{
buffer.append( "&update=true" );
}
if ( tag != null )
{
buffer.append( "&tag=" ).append( URLEncoder.encode( tag, charset ) );
}
return invoke( buffer.toString(), data );
}
/**
* Gets the HTTP Basic Authorization header value for the supplied username and password.
*
* @param username the username to use for authentication
* @param password the password to use for authentication
* @return the HTTP Basic Authorization header value
*/
private String toAuthorization( String username, String password )
{
StringBuffer buffer = new StringBuffer();
buffer.append( username ).append( ':' );
if ( password != null )
{
buffer.append( password );
}
return "Basic " + new String( Base64.encodeBase64( buffer.toString().getBytes() ) );
}
/**
* Reads all the data from the specified input stream and writes it to the specified output stream. Both streams are
* also closed.
*
* @param in the input stream to read from
* @param out the output stream to write to
* @throws IOException if an i/o error occurs
*/
private void pipe( InputStream in, OutputStream out )
throws IOException
{
out = new BufferedOutputStream( out );
int n;
byte[] bytes = new byte[1024 * 4];
while ( ( n = in.read( bytes ) ) != -1 )
{
out.write( bytes, 0, n );
}
out.flush();
out.close();
in.close();
}
/**
* Gets the data from the specified input stream as a string using the specified charset.
*
* @param in the input stream to read from
* @param charset the charset to use when constructing the string
* @return a string representation of the data read from the input stream
* @throws IOException if an i/o error occurs
*/
private String toString( InputStream in, String charset )
throws IOException
{
InputStreamReader reader = new InputStreamReader( in, charset );
StringBuffer buffer = new StringBuffer();
char[] chars = new char[1024];
int n;
while ( ( n = reader.read( chars, 0, chars.length ) ) != -1 )
{
buffer.append( chars, 0, n );
}
return buffer.toString();
}
}