Java tutorial
package org.apache.tomcat.maven.common.deployer; /* * 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 java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.URL; import java.net.URLEncoder; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Locale; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthCache; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.protocol.ClientContext; import org.apache.http.entity.AbstractHttpEntity; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.protocol.BasicHttpContext; /** * FIXME http connection tru a proxy * A Tomcat manager webapp invocation wrapper. * * @author Mark Hobson <markhobson@gmail.com> */ 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; /** * @since 2.0 */ private DefaultHttpClient httpClient; /** * @since 2.0 */ private BasicHttpContext localContext; /** * @since 2.2 */ private boolean verbose; // ---------------------------------------------------------------------- // 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, username, password, charset, true); } /** * 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 * @param verbose if the build is in verbose mode (quiet mode otherwise) * @since 2.2 */ public TomcatManager(URL url, String username, String password, String charset, boolean verbose) { this.url = url; this.username = username; this.password = password; this.charset = charset; this.verbose = verbose; PoolingClientConnectionManager poolingClientConnectionManager = new PoolingClientConnectionManager(); poolingClientConnectionManager.setMaxTotal(5); this.httpClient = new DefaultHttpClient(poolingClientConnectionManager); if (StringUtils.isNotEmpty(username)) { Credentials creds = new UsernamePasswordCredentials(username, password); String host = url.getHost(); int port = url.getPort() > -1 ? url.getPort() : AuthScope.ANY_PORT; httpClient.getCredentialsProvider().setCredentials(new AuthScope(host, port), creds); AuthCache authCache = new BasicAuthCache(); BasicScheme basicAuth = new BasicScheme(); HttpHost targetHost = new HttpHost(url.getHost(), url.getPort(), url.getProtocol()); authCache.put(targetHost, basicAuth); localContext = new BasicHttpContext(); localContext.setAttribute(ClientContext.AUTH_CACHE, authCache); } } // ---------------------------------------------------------------------- // 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse deploy(String path, File 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 TomcatManagerResponse deploy(String path, File 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 TomcatManagerResponse deploy(String path, File war, boolean update, String tag) throws TomcatManagerException, IOException { return deployImpl(path, null, null, war, update, tag); } /** * @param path * @param war * @param update * @param tag * @param length * @return * @throws TomcatManagerException * @throws IOException * @since 2.0 */ public TomcatManagerResponse deploy(String path, File war, boolean update, String tag, long length) throws TomcatManagerException, IOException { return deployImpl(path, null, null, war, update, tag, length); } /** * 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse 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 TomcatManagerResponse invoke(String path) throws TomcatManagerException, IOException { return invoke(path, null, -1); } // ---------------------------------------------------------------------- // Private Methods // ---------------------------------------------------------------------- private TomcatManagerResponse deployImpl(String path, URL config, URL war, File data, boolean update, String tag) throws TomcatManagerException, IOException { return deployImpl(path, config, war, data, update, tag, -1); } /** * 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 WAR file 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 TomcatManagerResponse deployImpl(String path, URL config, URL war, File data, boolean update, String tag, long length) throws TomcatManagerException, IOException { StringBuilder buffer = new StringBuilder("/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)); } if (update) { buffer.append("&update=true"); } if (tag != null) { buffer.append("&tag=").append(URLEncoder.encode(tag, charset)); } return invoke(buffer.toString(), data, length); } /** * Invokes Tomcat manager with the specified command and content data. * * @param path the Tomcat manager command to invoke * @param data file to deploy * @return the Tomcat manager response * @throws TomcatManagerException if the Tomcat manager request fails * @throws IOException if an i/o error occurs */ protected TomcatManagerResponse invoke(String path, File data, long length) throws TomcatManagerException, IOException { HttpRequestBase httpRequestBase = null; if (data == null) { httpRequestBase = new HttpGet(url + path); } else { HttpPut httpPut = new HttpPut(url + path); httpPut.setEntity(new RequestEntityImplementation(data, length, url + path, verbose)); httpRequestBase = httpPut; } if (userAgent != null) { httpRequestBase.setHeader("User-Agent", userAgent); } HttpResponse response = httpClient.execute(httpRequestBase, localContext); int statusCode = response.getStatusLine().getStatusCode(); switch (statusCode) { // Success Codes case HttpStatus.SC_OK: // 200 case HttpStatus.SC_CREATED: // 201 case HttpStatus.SC_ACCEPTED: // 202 break; // handle all redirect even if http specs says " the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user" case HttpStatus.SC_MOVED_PERMANENTLY: // 301 case HttpStatus.SC_MOVED_TEMPORARILY: // 302 case HttpStatus.SC_SEE_OTHER: // 303 String relocateUrl = calculateRelocatedUrl(response); this.url = new URL(relocateUrl); return invoke(path, data, length); } return new TomcatManagerResponse().setStatusCode(response.getStatusLine().getStatusCode()) .setReasonPhrase(response.getStatusLine().getReasonPhrase()) .setHttpResponseBody(IOUtils.toString(response.getEntity().getContent())); } protected String calculateRelocatedUrl(HttpResponse response) { Header locationHeader = response.getFirstHeader("Location"); String locationField = locationHeader.getValue(); // is it a relative Location or a full ? return locationField.startsWith("http") ? locationField : url.toString() + '/' + locationField; } /** * 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())); } private final class RequestEntityImplementation extends AbstractHttpEntity { private final static int BUFFER_SIZE = 2048; private File file; PrintStream out = System.out; private long length = -1; private int lastLength; private String url; private long startTime; private boolean verbose; private RequestEntityImplementation(final File file, long length, String url, boolean verbose) { this.file = file; this.length = length; this.url = url; this.verbose = verbose; } public long getContentLength() { return length >= 0 ? length : (file.length() >= 0 ? file.length() : -1); } public InputStream getContent() throws IOException, IllegalStateException { return new FileInputStream(this.file); } public boolean isRepeatable() { return true; } public void writeTo(final OutputStream outstream) throws IOException { long completed = 0; if (outstream == null) { throw new IllegalArgumentException("Output stream may not be null"); } FileInputStream stream = new FileInputStream(this.file); transferInitiated(this.url); this.startTime = System.currentTimeMillis(); try { byte[] buffer = new byte[BUFFER_SIZE]; int l; if (this.length < 0) { // until EOF while ((l = stream.read(buffer)) != -1) { transferProgressed(completed += buffer.length, -1); outstream.write(buffer, 0, l); } } else { // no need to consume more than length long remaining = this.length; while (remaining > 0) { int transferSize = (int) Math.min(BUFFER_SIZE, remaining); completed += transferSize; l = stream.read(buffer, 0, transferSize); if (l == -1) { break; } outstream.write(buffer, 0, l); remaining -= l; transferProgressed(completed, this.length); } } transferSucceeded(completed); } finally { stream.close(); out.println(); } // end transfer } public boolean isStreaming() { return true; } public void transferInitiated(String url) { String message = "Uploading"; out.println(message + ": " + url); } public void transferProgressed(long completedSize, long totalSize) { if (!verbose) { return; } StringBuilder buffer = new StringBuilder(64); buffer.append(getStatus(completedSize, totalSize)).append(" "); lastLength = buffer.length(); buffer.append('\r'); out.print(buffer); } public void transferSucceeded(long contentLength) { if (contentLength >= 0) { String type = "Uploaded"; String len = contentLength >= 1024 ? toKB(contentLength) + " KB" : contentLength + " B"; String throughput = ""; long duration = System.currentTimeMillis() - startTime; if (duration > 0) { DecimalFormat format = new DecimalFormat("0.0", new DecimalFormatSymbols(Locale.ENGLISH)); double kbPerSec = (contentLength / 1024.0) / (duration / 1000.0); throughput = " at " + format.format(kbPerSec) + " KB/sec"; } out.println(type + ": " + url + " (" + len + throughput + ")"); } } private String getStatus(long complete, long total) { if (total >= 1024) { return toKB(complete) + "/" + toKB(total) + " KB "; } else if (total >= 0) { return complete + "/" + total + " B "; } else if (complete >= 1024) { return toKB(complete) + " KB "; } else { return complete + " B "; } } private long toKB(long bytes) { return (bytes + 1023) / 1024; } } }