View Javadoc

1   package org.codehaus.mojo.tomcat;
2   
3   /*
4    * Copyright 2005 Mark Hobson.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import org.apache.commons.codec.binary.Base64;
20  
21  import java.io.BufferedOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.InputStreamReader;
25  import java.io.OutputStream;
26  import java.net.HttpURLConnection;
27  import java.net.URL;
28  import java.net.URLEncoder;
29  
30  /**
31   * A Tomcat manager webapp invocation wrapper.
32   *
33   * @author Mark Hobson <markhobson@gmail.com>
34   * @version $Id: TomcatManager.java 6588 2008-03-28 12:22:57Z bentmann $
35   */
36  public class TomcatManager
37  {
38      // ----------------------------------------------------------------------
39      // Constants
40      // ----------------------------------------------------------------------
41  
42      /**
43       * The charset to use when decoding Tomcat manager responses.
44       */
45      private static final String MANAGER_CHARSET = "UTF-8";
46  
47      // ----------------------------------------------------------------------
48      // Fields
49      // ----------------------------------------------------------------------
50  
51      /**
52       * The full URL of the Tomcat manager instance to use.
53       */
54      private URL url;
55  
56      /**
57       * The username to use when authenticating with Tomcat manager.
58       */
59      private String username;
60  
61      /**
62       * The password to use when authenticating with Tomcat manager.
63       */
64      private String password;
65  
66      /**
67       * The URL encoding charset to use when communicating with Tomcat manager.
68       */
69      private String charset;
70  
71      /**
72       * The user agent name to use when communicating with Tomcat manager.
73       */
74      private String userAgent;
75  
76      // ----------------------------------------------------------------------
77      // Constructors
78      // ----------------------------------------------------------------------
79  
80      /**
81       * Creates a Tomcat manager wrapper for the specified URL that uses a
82       * username of <code>admin</code>, an empty password and ISO-8859-1 URL
83       * encoding.
84       *
85       * @param url the full URL of the Tomcat manager instance to use
86       */
87      public TomcatManager( URL url )
88      {
89          this( url, "admin" );
90      }
91  
92      /**
93       * Creates a Tomcat manager wrapper for the specified URL and username that
94       * uses an empty password and ISO-8859-1 URL encoding.
95       *
96       * @param url      the full URL of the Tomcat manager instance to use
97       * @param username the username to use when authenticating with Tomcat manager
98       */
99      public TomcatManager( URL url, String username )
100     {
101         this( url, username, "" );
102     }
103 
104     /**
105      * Creates a Tomcat manager wrapper for the specified URL, username and
106      * password that uses ISO-8859-1 URL encoding.
107      *
108      * @param url      the full URL of the Tomcat manager instance to use
109      * @param username the username to use when authenticating with Tomcat manager
110      * @param password the password to use when authenticating with Tomcat manager
111      */
112     public TomcatManager( URL url, String username, String password )
113     {
114         this( url, username, password, "ISO-8859-1" );
115     }
116 
117     /**
118      * Creates a Tomcat manager wrapper for the specified URL, username,
119      * password and URL encoding.
120      *
121      * @param url      the full URL of the Tomcat manager instance to use
122      * @param username the username to use when authenticating with Tomcat manager
123      * @param password the password to use when authenticating with Tomcat manager
124      * @param charset  the URL encoding charset to use when communicating with
125      *                 Tomcat manager
126      */
127     public TomcatManager( URL url, String username, String password, String charset )
128     {
129         this.url = url;
130         this.username = username;
131         this.password = password;
132         this.charset = charset;
133     }
134 
135     // ----------------------------------------------------------------------
136     // Public Methods
137     // ----------------------------------------------------------------------
138 
139     /**
140      * Gets the full URL of the Tomcat manager instance.
141      *
142      * @return the full URL of the Tomcat manager instance
143      */
144     public URL getURL()
145     {
146         return url;
147     }
148 
149     /**
150      * Gets the username to use when authenticating with Tomcat manager.
151      *
152      * @return the username to use when authenticating with Tomcat manager
153      */
154     public String getUserName()
155     {
156         return username;
157     }
158 
159     /**
160      * Gets the password to use when authenticating with Tomcat manager.
161      *
162      * @return the password to use when authenticating with Tomcat manager
163      */
164     public String getPassword()
165     {
166         return password;
167     }
168 
169     /**
170      * Gets the URL encoding charset to use when communicating with Tomcat
171      * manager.
172      *
173      * @return the URL encoding charset to use when communicating with Tomcat
174      *         manager
175      */
176     public String getCharset()
177     {
178         return charset;
179     }
180 
181     /**
182      * Gets the user agent name to use when communicating with Tomcat manager.
183      *
184      * @return the user agent name to use when communicating with Tomcat
185      *         manager
186      */
187     public String getUserAgent()
188     {
189         return userAgent;
190     }
191 
192     /**
193      * Sets the user agent name to use when communicating with Tomcat manager.
194      *
195      * @param userAgent the user agent name to use when communicating with Tomcat
196      *                  manager
197      */
198     public void setUserAgent( String userAgent )
199     {
200         this.userAgent = userAgent;
201     }
202 
203     /**
204      * Deploys the specified WAR as a URL to the specified context path.
205      *
206      * @param path the webapp context path to deploy to
207      * @param war  the URL of the WAR to deploy
208      * @return the Tomcat manager response
209      * @throws TomcatManagerException if the Tomcat manager request fails
210      * @throws IOException            if an i/o error occurs
211      */
212     public String deploy( String path, URL war )
213         throws TomcatManagerException, IOException
214     {
215         return deploy( path, war, false );
216     }
217 
218     /**
219      * Deploys the specified WAR as a URL to the specified context path,
220      * optionally undeploying the webapp if it already exists.
221      *
222      * @param path   the webapp context path to deploy to
223      * @param war    the URL of the WAR to deploy
224      * @param update whether to first undeploy the webapp if it already exists
225      * @return the Tomcat manager response
226      * @throws TomcatManagerException if the Tomcat manager request fails
227      * @throws IOException            if an i/o error occurs
228      */
229     public String deploy( String path, URL war, boolean update )
230         throws TomcatManagerException, IOException
231     {
232         return deploy( path, war, update, null );
233     }
234 
235     /**
236      * Deploys the specified WAR as a URL to the specified context path,
237      * optionally undeploying the webapp if it already exists and using the
238      * specified tag name.
239      *
240      * @param path   the webapp context path to deploy to
241      * @param war    the URL of the WAR to deploy
242      * @param update whether to first undeploy the webapp if it already exists
243      * @param tag    the tag name to use
244      * @return the Tomcat manager response
245      * @throws TomcatManagerException if the Tomcat manager request fails
246      * @throws IOException            if an i/o error occurs
247      */
248     public String deploy( String path, URL war, boolean update, String tag )
249         throws TomcatManagerException, IOException
250     {
251         return deployImpl( path, null, war, null, update, tag );
252     }
253 
254     /**
255      * Deploys the specified WAR as a HTTP PUT to the specified context path.
256      *
257      * @param path the webapp context path to deploy to
258      * @param war  an input stream to the WAR to deploy
259      * @return the Tomcat manager response
260      * @throws TomcatManagerException if the Tomcat manager request fails
261      * @throws IOException            if an i/o error occurs
262      */
263     public String deploy( String path, InputStream war )
264         throws TomcatManagerException, IOException
265     {
266         return deploy( path, war, false );
267     }
268 
269     /**
270      * Deploys the specified WAR as a HTTP PUT to the specified context path,
271      * optionally undeploying the webapp if it already exists.
272      *
273      * @param path   the webapp context path to deploy to
274      * @param war    an input stream to the WAR to deploy
275      * @param update whether to first undeploy the webapp if it already exists
276      * @return the Tomcat manager response
277      * @throws TomcatManagerException if the Tomcat manager request fails
278      * @throws IOException            if an i/o error occurs
279      */
280     public String deploy( String path, InputStream war, boolean update )
281         throws TomcatManagerException, IOException
282     {
283         return deploy( path, war, update, null );
284     }
285 
286     /**
287      * Deploys the specified WAR as a HTTP PUT to the specified context path,
288      * optionally undeploying the webapp if it already exists and using the
289      * specified tag name.
290      *
291      * @param path   the webapp context path to deploy to
292      * @param war    an input stream to the WAR to deploy
293      * @param update whether to first undeploy the webapp if it already exists
294      * @param tag    the tag name to use
295      * @return the Tomcat manager response
296      * @throws TomcatManagerException if the Tomcat manager request fails
297      * @throws IOException            if an i/o error occurs
298      */
299     public String deploy( String path, InputStream war, boolean update, String tag )
300         throws TomcatManagerException, IOException
301     {
302         return deployImpl( path, null, null, war, update, tag );
303     }
304 
305     /**
306      * Deploys the specified context XML configuration to the specified context
307      * path.
308      *
309      * @param path   the webapp context path to deploy to
310      * @param config the URL of the context XML configuration to deploy
311      * @return the Tomcat manager response
312      * @throws TomcatManagerException if the Tomcat manager request fails
313      * @throws IOException            if an i/o error occurs
314      */
315     public String deployContext( String path, URL config )
316         throws TomcatManagerException, IOException
317     {
318         return deployContext( path, config, false );
319     }
320 
321     /**
322      * Deploys the specified context XML configuration to the specified context
323      * path, optionally undeploying the webapp if it already exists.
324      *
325      * @param path   the webapp context path to deploy to
326      * @param config the URL of the context XML configuration to deploy
327      * @param update whether to first undeploy the webapp if it already exists
328      * @return the Tomcat manager response
329      * @throws TomcatManagerException if the Tomcat manager request fails
330      * @throws IOException            if an i/o error occurs
331      */
332     public String deployContext( String path, URL config, boolean update )
333         throws TomcatManagerException, IOException
334     {
335         return deployContext( path, config, update, null );
336     }
337 
338     /**
339      * Deploys the specified context XML configuration to the specified context
340      * path, optionally undeploying the webapp if it already exists and using
341      * the specified tag name.
342      *
343      * @param path   the webapp context path to deploy to
344      * @param config the URL of the context XML configuration to deploy
345      * @param update whether to first undeploy the webapp if it already exists
346      * @param tag    the tag name to use
347      * @return the Tomcat manager response
348      * @throws TomcatManagerException if the Tomcat manager request fails
349      * @throws IOException            if an i/o error occurs
350      */
351     public String deployContext( String path, URL config, boolean update, String tag )
352         throws TomcatManagerException, IOException
353     {
354         return deployContext( path, config, null, update, tag );
355     }
356 
357     /**
358      * Deploys the specified context XML configuration and WAR as a URL to the
359      * specified context path.
360      *
361      * @param path   the webapp context path to deploy to
362      * @param config the URL of the context XML configuration to deploy
363      * @param war    the URL of the WAR to deploy
364      * @return the Tomcat manager response
365      * @throws TomcatManagerException if the Tomcat manager request fails
366      * @throws IOException            if an i/o error occurs
367      */
368     public String deployContext( String path, URL config, URL war )
369         throws TomcatManagerException, IOException
370     {
371         return deployContext( path, config, war, false );
372     }
373 
374     /**
375      * Deploys the specified context XML configuration and WAR as a URL to the
376      * specified context path, optionally undeploying the webapp if it already
377      * exists.
378      *
379      * @param path   the webapp context path to deploy to
380      * @param config the URL of the context XML configuration to deploy
381      * @param war    the URL of the WAR to deploy
382      * @param update whether to first undeploy the webapp if it already exists
383      * @return the Tomcat manager response
384      * @throws TomcatManagerException if the Tomcat manager request fails
385      * @throws IOException            if an i/o error occurs
386      */
387     public String deployContext( String path, URL config, URL war, boolean update )
388         throws TomcatManagerException, IOException
389     {
390         return deployContext( path, config, war, update, null );
391     }
392 
393     /**
394      * Deploys the specified context XML configuration and WAR as a URL to the
395      * specified context path, optionally undeploying the webapp if it already
396      * exists and using the specified tag name.
397      *
398      * @param path   the webapp context path to deploy to
399      * @param config the URL of the context XML configuration to deploy
400      * @param war    the URL of the WAR to deploy
401      * @param update whether to first undeploy the webapp if it already exists
402      * @param tag    the tag name to use
403      * @return the Tomcat manager response
404      * @throws TomcatManagerException if the Tomcat manager request fails
405      * @throws IOException            if an i/o error occurs
406      */
407     public String deployContext( String path, URL config, URL war, boolean update, String tag )
408         throws TomcatManagerException, IOException
409     {
410         return deployImpl( path, config, war, null, update, tag );
411     }
412 
413     /**
414      * Undeploys the webapp at the specified context path.
415      *
416      * @param path the webapp context path to undeploy
417      * @return the Tomcat manager response
418      * @throws TomcatManagerException if the Tomcat manager request fails
419      * @throws IOException            if an i/o error occurs
420      */
421     public String undeploy( String path )
422         throws TomcatManagerException, IOException
423     {
424         return invoke( "/undeploy?path=" + URLEncoder.encode( path, charset ) );
425     }
426 
427     /**
428      * Reloads the webapp at the specified context path.
429      *
430      * @param path the webapp context path to reload
431      * @return the Tomcat manager response
432      * @throws TomcatManagerException if the Tomcat manager request fails
433      * @throws IOException            if an i/o error occurs
434      */
435     public String reload( String path )
436         throws TomcatManagerException, IOException
437     {
438         return invoke( "/reload?path=" + URLEncoder.encode( path, charset ) );
439     }
440 
441     /**
442      * Starts the webapp at the specified context path.
443      *
444      * @param path the webapp context path to start
445      * @return the Tomcat manager response
446      * @throws TomcatManagerException if the Tomcat manager request fails
447      * @throws IOException            if an i/o error occurs
448      */
449     public String start( String path )
450         throws TomcatManagerException, IOException
451     {
452         return invoke( "/start?path=" + URLEncoder.encode( path, charset ) );
453     }
454 
455     /**
456      * Stops the webapp at the specified context path.
457      *
458      * @param path the webapp context path to stop
459      * @return the Tomcat manager response
460      * @throws TomcatManagerException if the Tomcat manager request fails
461      * @throws IOException            if an i/o error occurs
462      */
463     public String stop( String path )
464         throws TomcatManagerException, IOException
465     {
466         return invoke( "/stop?path=" + URLEncoder.encode( path, charset ) );
467     }
468 
469     /**
470      * Lists all the currently deployed web applications. 
471      * 
472      * @return the list of currently deployed applications
473      * @throws TomcatManagerException if the Tomcat manager request fails
474      * @throws IOException            if an i/o error occurs
475      */
476     public String list()
477         throws TomcatManagerException, IOException
478     {
479         return invoke( "/list" );
480     }
481 
482     /**
483      * Lists information about the Tomcat version, OS, and JVM properties.
484      * 
485      * @return the server information
486      * @throws TomcatManagerException if the Tomcat manager request fails
487      * @throws IOException            if an i/o error occurs
488      */
489     public String getServerInfo()
490         throws TomcatManagerException, IOException
491     {
492         return invoke( "/serverinfo" );
493     }
494 
495     /**
496      * Lists all of the global JNDI resources.
497      * 
498      * @return the list of all global JNDI resources
499      * @throws TomcatManagerException if the Tomcat manager request fails
500      * @throws IOException            if an i/o error occurs
501      */
502     public String getResources()
503         throws TomcatManagerException, IOException
504     {
505         return getResources( null );
506     }
507 
508     /**
509      * Lists the global JNDI resources of the given type.
510      * 
511      * @param type the class name of the resources to list, or <code>null</code>
512      *             for all
513      * @return the list of global JNDI resources of the given type
514      * @throws TomcatManagerException if the Tomcat manager request fails
515      * @throws IOException            if an i/o error occurs
516      */
517     public String getResources( String type )
518         throws TomcatManagerException, IOException
519     {
520         StringBuffer buffer = new StringBuffer();
521         buffer.append( "/resources" );
522 
523         if ( type != null )
524             buffer.append( "?type=" + URLEncoder.encode( type, charset ) );
525 
526         return invoke( buffer.toString() );
527     }
528 
529     /**
530      * Lists the security role names and corresponding descriptions that are
531      * available.
532      * 
533      * @return the list of security role names and corresponding descriptions
534      * @throws TomcatManagerException if the Tomcat manager request fails
535      * @throws IOException            if an i/o error occurs
536      */
537     public String getRoles()
538         throws TomcatManagerException, IOException
539     {
540         return invoke( "/roles" );
541     }
542 
543     /**
544      * Lists the default session timeout and the number of currently active
545      * sessions for the given context path.
546      * 
547      * @param path the context path to list session information for
548      * @return the default session timeout and the number of currently active
549      *         sessions 
550      * @throws TomcatManagerException if the Tomcat manager request fails
551      * @throws IOException            if an i/o error occurs
552      */
553     public String getSessions( String path )
554         throws TomcatManagerException, IOException
555     {
556         return invoke( "/sessions?path=" + URLEncoder.encode( path, charset ) );
557     }
558 
559     // ----------------------------------------------------------------------
560     // Protected Methods
561     // ----------------------------------------------------------------------
562 
563     /**
564      * Invokes Tomcat manager with the specified command.
565      *
566      * @param path the Tomcat manager command to invoke
567      * @return the Tomcat manager response
568      * @throws TomcatManagerException if the Tomcat manager request fails
569      * @throws IOException            if an i/o error occurs
570      */
571     protected String invoke( String path )
572         throws TomcatManagerException, IOException
573     {
574         return invoke( path, null );
575     }
576 
577     /**
578      * Invokes Tomcat manager with the specified command and content data.
579      *
580      * @param path the Tomcat manager command to invoke
581      * @param data an input stream to the content data
582      * @return the Tomcat manager response
583      * @throws TomcatManagerException if the Tomcat manager request fails
584      * @throws IOException            if an i/o error occurs
585      */
586     protected String invoke( String path, InputStream data )
587         throws TomcatManagerException, IOException
588     {
589         HttpURLConnection connection = (HttpURLConnection) new URL( url + path ).openConnection();
590         connection.setAllowUserInteraction( false );
591         connection.setDoInput( true );
592         connection.setUseCaches( false );
593 
594         if ( data == null )
595         {
596             connection.setDoOutput( false );
597             connection.setRequestMethod( "GET" );
598         }
599         else
600         {
601             connection.setDoOutput( true );
602             connection.setRequestMethod( "PUT" );
603             connection.setRequestProperty( "Content-Type", "application/octet-stream" );
604         }
605 
606         if ( userAgent != null )
607         {
608             connection.setRequestProperty( "User-Agent", userAgent );
609         }
610         connection.setRequestProperty( "Authorization", toAuthorization( username, password ) );
611 
612         connection.connect();
613 
614         if ( data != null )
615         {
616             pipe( data, connection.getOutputStream() );
617         }
618 
619         String response = toString( connection.getInputStream(), MANAGER_CHARSET );
620 
621         if ( !response.startsWith( "OK -" ) )
622         {
623             throw new TomcatManagerException( response );
624         }
625 
626         return response;
627     }
628 
629     // ----------------------------------------------------------------------
630     // Private Methods
631     // ----------------------------------------------------------------------
632 
633     /**
634      * Deploys the specified WAR.
635      *
636      * @param path   the webapp context path to deploy to
637      * @param config the URL of the context XML configuration to deploy, or null
638      *               for none
639      * @param war    the URL of the WAR to deploy, or null to use
640      *               <code>data</code>
641      * @param data   an input stream to the WAR to deploy, or null to use
642      *               <code>war</code>
643      * @param update whether to first undeploy the webapp if it already exists
644      * @param tag    the tag name to use
645      * @return the Tomcat manager response
646      * @throws TomcatManagerException if the Tomcat manager request fails
647      * @throws IOException            if an i/o error occurs
648      */
649     private String deployImpl( String path, URL config, URL war, InputStream data, boolean update, String tag )
650         throws TomcatManagerException, IOException
651     {
652         StringBuffer buffer = new StringBuffer( "/deploy" );
653         buffer.append( "?path=" ).append( URLEncoder.encode( path, charset ) );
654 
655         if ( config != null )
656         {
657             buffer.append( "&config=" ).append( URLEncoder.encode( config.toString(), charset ) );
658         }
659 
660         if ( war != null )
661         {
662             buffer.append( "&war=" ).append( URLEncoder.encode( war.toString(), charset ) );
663         }
664         else
665         {
666             // for Tomcat 5.0.27
667             buffer.append( "&war=" );
668         }
669 
670         if ( update )
671         {
672             buffer.append( "&update=true" );
673         }
674 
675         if ( tag != null )
676         {
677             buffer.append( "&tag=" ).append( URLEncoder.encode( tag, charset ) );
678         }
679 
680         return invoke( buffer.toString(), data );
681     }
682 
683     /**
684      * Gets the HTTP Basic Authorization header value for the supplied username
685      * and password.
686      *
687      * @param username the username to use for authentication
688      * @param password the password to use for authentication
689      * @return the HTTP Basic Authorization header value
690      */
691     private String toAuthorization( String username, String password )
692     {
693         StringBuffer buffer = new StringBuffer();
694         buffer.append( username ).append( ':' );
695         if ( password != null )
696         {
697             buffer.append( password );
698         }
699         return "Basic " + new String( Base64.encodeBase64( buffer.toString().getBytes() ) );
700     }
701 
702     /**
703      * Reads all the data from the specified input stream and writes it to the
704      * specified output stream.  Both streams are also closed.
705      *
706      * @param in  the input stream to read from
707      * @param out the output stream to write to
708      * @throws IOException if an i/o error occurs
709      */
710     private void pipe( InputStream in, OutputStream out )
711         throws IOException
712     {
713         out = new BufferedOutputStream( out );
714         int n;
715         byte[] bytes = new byte[1024 * 4];
716         while ( ( n = in.read( bytes ) ) != -1 )
717         {
718             out.write( bytes, 0, n );
719         }
720         out.flush();
721         out.close();
722         in.close();
723     }
724 
725     /**
726      * Gets the data from the specified input stream as a string using the
727      * specified charset.
728      *
729      * @param in      the input stream to read from
730      * @param charset the charset to use when constructing the string
731      * @return a string representation of the data read from the input stream
732      * @throws IOException if an i/o error occurs
733      */
734     private String toString( InputStream in, String charset )
735         throws IOException
736     {
737         InputStreamReader reader = new InputStreamReader( in, charset );
738 
739         StringBuffer buffer = new StringBuffer();
740         char[] chars = new char[1024];
741         int n;
742         while ( ( n = reader.read( chars, 0, chars.length ) ) != -1 )
743         {
744             buffer.append( chars, 0, n );
745         }
746 
747         return buffer.toString();
748     }
749 }