1 package org.apache.tomcat.maven.common.deployer;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import org.apache.commons.codec.binary.Base64;
23 import org.apache.commons.io.IOUtils;
24 import org.apache.commons.lang.StringUtils;
25 import org.apache.http.Header;
26 import org.apache.http.HttpHost;
27 import org.apache.http.HttpResponse;
28 import org.apache.http.HttpStatus;
29 import org.apache.http.auth.AuthScope;
30 import org.apache.http.auth.Credentials;
31 import org.apache.http.auth.UsernamePasswordCredentials;
32 import org.apache.http.client.AuthCache;
33 import org.apache.http.client.methods.HttpGet;
34 import org.apache.http.client.methods.HttpPut;
35 import org.apache.http.client.methods.HttpRequestBase;
36 import org.apache.http.client.protocol.ClientContext;
37 import org.apache.http.entity.AbstractHttpEntity;
38 import org.apache.http.impl.auth.BasicScheme;
39 import org.apache.http.impl.client.BasicAuthCache;
40 import org.apache.http.impl.client.DefaultHttpClient;
41 import org.apache.http.impl.conn.PoolingClientConnectionManager;
42 import org.apache.http.protocol.BasicHttpContext;
43
44 import java.io.File;
45 import java.io.FileInputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.OutputStream;
49 import java.io.PrintStream;
50 import java.net.URL;
51 import java.net.URLEncoder;
52 import java.text.DecimalFormat;
53 import java.text.DecimalFormatSymbols;
54 import java.util.Locale;
55
56 /**
57 * FIXME http connection tru a proxy
58 * A Tomcat manager webapp invocation wrapper.
59 *
60 * @author Mark Hobson <markhobson@gmail.com>
61 */
62 public class TomcatManager
63 {
64 // ----------------------------------------------------------------------
65 // Constants
66 // ----------------------------------------------------------------------
67
68 /**
69 * The charset to use when decoding Tomcat manager responses.
70 */
71 private static final String MANAGER_CHARSET = "UTF-8";
72
73 // ----------------------------------------------------------------------
74 // Fields
75 // ----------------------------------------------------------------------
76
77 /**
78 * The full URL of the Tomcat manager instance to use.
79 */
80 private URL url;
81
82 /**
83 * The username to use when authenticating with Tomcat manager.
84 */
85 private String username;
86
87 /**
88 * The password to use when authenticating with Tomcat manager.
89 */
90 private String password;
91
92 /**
93 * The URL encoding charset to use when communicating with Tomcat manager.
94 */
95 private String charset;
96
97 /**
98 * The user agent name to use when communicating with Tomcat manager.
99 */
100 private String userAgent;
101
102 /**
103 * @since 2.0
104 */
105 private DefaultHttpClient httpClient;
106
107 /**
108 * @since 2.0
109 */
110 private BasicHttpContext localContext;
111
112 /**
113 * @since 2.2
114 */
115 private boolean verbose;
116
117 // ----------------------------------------------------------------------
118 // Constructors
119 // ----------------------------------------------------------------------
120
121 /**
122 * Creates a Tomcat manager wrapper for the specified URL that uses a username of <code>admin</code>, an empty
123 * password and ISO-8859-1 URL encoding.
124 *
125 * @param url the full URL of the Tomcat manager instance to use
126 */
127 public TomcatManager( URL url )
128 {
129 this( url, "admin" );
130 }
131
132 /**
133 * Creates a Tomcat manager wrapper for the specified URL and username that uses an empty password and ISO-8859-1
134 * URL encoding.
135 *
136 * @param url the full URL of the Tomcat manager instance to use
137 * @param username the username to use when authenticating with Tomcat manager
138 */
139 public TomcatManager( URL url, String username )
140 {
141 this( url, username, "" );
142 }
143
144 /**
145 * Creates a Tomcat manager wrapper for the specified URL, username and password that uses ISO-8859-1 URL encoding.
146 *
147 * @param url the full URL of the Tomcat manager instance to use
148 * @param username the username to use when authenticating with Tomcat manager
149 * @param password the password to use when authenticating with Tomcat manager
150 */
151 public TomcatManager( URL url, String username, String password )
152 {
153 this( url, username, password, "ISO-8859-1" );
154 }
155
156
157 /**
158 * Creates a Tomcat manager wrapper for the specified URL, username, password and URL encoding.
159 *
160 * @param url the full URL of the Tomcat manager instance to use
161 * @param username the username to use when authenticating with Tomcat manager
162 * @param password the password to use when authenticating with Tomcat manager
163 * @param charset the URL encoding charset to use when communicating with Tomcat manager
164 */
165 public TomcatManager( URL url, String username, String password, String charset )
166 {
167 this( url, username, password, charset, true );
168 }
169
170 /**
171 * Creates a Tomcat manager wrapper for the specified URL, username, password and URL encoding.
172 *
173 * @param url the full URL of the Tomcat manager instance to use
174 * @param username the username to use when authenticating with Tomcat manager
175 * @param password the password to use when authenticating with Tomcat manager
176 * @param charset the URL encoding charset to use when communicating with Tomcat manager
177 * @param verbose if the build is in verbose mode (quiet mode otherwise)
178 * @since 2.2
179 */
180 public TomcatManager( URL url, String username, String password, String charset, boolean verbose )
181 {
182 this.url = url;
183 this.username = username;
184 this.password = password;
185 this.charset = charset;
186 this.verbose = verbose;
187
188 PoolingClientConnectionManager poolingClientConnectionManager = new PoolingClientConnectionManager();
189 poolingClientConnectionManager.setMaxTotal( 5 );
190 this.httpClient = new DefaultHttpClient( poolingClientConnectionManager );
191 if ( StringUtils.isNotEmpty( username ) )
192 {
193 Credentials creds = new UsernamePasswordCredentials( username, password );
194
195 String host = url.getHost();
196 int port = url.getPort() > -1 ? url.getPort() : AuthScope.ANY_PORT;
197
198 httpClient.getCredentialsProvider().setCredentials( new AuthScope( host, port ), creds );
199
200 AuthCache authCache = new BasicAuthCache();
201 BasicScheme basicAuth = new BasicScheme();
202 HttpHost targetHost = new HttpHost( url.getHost(), url.getPort(), url.getProtocol() );
203 authCache.put( targetHost, basicAuth );
204
205 localContext = new BasicHttpContext();
206 localContext.setAttribute( ClientContext.AUTH_CACHE, authCache );
207 }
208 }
209
210 // ----------------------------------------------------------------------
211 // Public Methods
212 // ----------------------------------------------------------------------
213
214 /**
215 * Gets the full URL of the Tomcat manager instance.
216 *
217 * @return the full URL of the Tomcat manager instance
218 */
219 public URL getURL()
220 {
221 return url;
222 }
223
224 /**
225 * Gets the username to use when authenticating with Tomcat manager.
226 *
227 * @return the username to use when authenticating with Tomcat manager
228 */
229 public String getUserName()
230 {
231 return username;
232 }
233
234 /**
235 * Gets the password to use when authenticating with Tomcat manager.
236 *
237 * @return the password to use when authenticating with Tomcat manager
238 */
239 public String getPassword()
240 {
241 return password;
242 }
243
244 /**
245 * Gets the URL encoding charset to use when communicating with Tomcat manager.
246 *
247 * @return the URL encoding charset to use when communicating with Tomcat manager
248 */
249 public String getCharset()
250 {
251 return charset;
252 }
253
254 /**
255 * Gets the user agent name to use when communicating with Tomcat manager.
256 *
257 * @return the user agent name to use when communicating with Tomcat manager
258 */
259 public String getUserAgent()
260 {
261 return userAgent;
262 }
263
264 /**
265 * Sets the user agent name to use when communicating with Tomcat manager.
266 *
267 * @param userAgent the user agent name to use when communicating with Tomcat manager
268 */
269 public void setUserAgent( String userAgent )
270 {
271 this.userAgent = userAgent;
272 }
273
274 /**
275 * Deploys the specified WAR as a URL to the specified context path.
276 *
277 * @param path the webapp context path to deploy to
278 * @param war the URL of the WAR to deploy
279 * @return the Tomcat manager response
280 * @throws TomcatManagerException if the Tomcat manager request fails
281 * @throws IOException if an i/o error occurs
282 */
283 public TomcatManagerResponse deploy( String path, URL war )
284 throws TomcatManagerException, IOException
285 {
286 return deploy( path, war, false );
287 }
288
289 /**
290 * Deploys the specified WAR as a URL to the specified context path, optionally undeploying the webapp if it already
291 * exists.
292 *
293 * @param path the webapp context path to deploy to
294 * @param war the URL of the WAR to deploy
295 * @param update whether to first undeploy the webapp if it already exists
296 * @return the Tomcat manager response
297 * @throws TomcatManagerException if the Tomcat manager request fails
298 * @throws IOException if an i/o error occurs
299 */
300 public TomcatManagerResponse deploy( String path, URL war, boolean update )
301 throws TomcatManagerException, IOException
302 {
303 return deploy( path, war, update, null );
304 }
305
306 /**
307 * Deploys the specified WAR as a URL to the specified context path, optionally undeploying the webapp if it already
308 * exists and using the specified tag name.
309 *
310 * @param path the webapp context path to deploy to
311 * @param war the URL of the WAR to deploy
312 * @param update whether to first undeploy the webapp if it already exists
313 * @param tag the tag name to use
314 * @return the Tomcat manager response
315 * @throws TomcatManagerException if the Tomcat manager request fails
316 * @throws IOException if an i/o error occurs
317 */
318 public TomcatManagerResponse deploy( String path, URL war, boolean update, String tag )
319 throws TomcatManagerException, IOException
320 {
321 return deployImpl( path, null, war, null, update, tag );
322 }
323
324 /**
325 * Deploys the specified WAR as a HTTP PUT to the specified context path.
326 *
327 * @param path the webapp context path to deploy to
328 * @param war an input stream to the WAR to deploy
329 * @return the Tomcat manager response
330 * @throws TomcatManagerException if the Tomcat manager request fails
331 * @throws IOException if an i/o error occurs
332 */
333 public TomcatManagerResponse deploy( String path, File war )
334 throws TomcatManagerException, IOException
335 {
336 return deploy( path, war, false );
337 }
338
339 /**
340 * Deploys the specified WAR as a HTTP PUT to the specified context path, optionally undeploying the webapp if it
341 * already exists.
342 *
343 * @param path the webapp context path to deploy to
344 * @param war an input stream to the WAR to deploy
345 * @param update whether to first undeploy the webapp if it already exists
346 * @return the Tomcat manager response
347 * @throws TomcatManagerException if the Tomcat manager request fails
348 * @throws IOException if an i/o error occurs
349 */
350 public TomcatManagerResponse deploy( String path, File war, boolean update )
351 throws TomcatManagerException, IOException
352 {
353 return deploy( path, war, update, null );
354 }
355
356 /**
357 * Deploys the specified WAR as a HTTP PUT to the specified context path, optionally undeploying the webapp if it
358 * already exists and using the specified tag name.
359 *
360 * @param path the webapp context path to deploy to
361 * @param war an input stream to the WAR to deploy
362 * @param update whether to first undeploy the webapp if it already exists
363 * @param tag the tag name to use
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 TomcatManagerResponse deploy( String path, File war, boolean update, String tag )
369 throws TomcatManagerException, IOException
370 {
371 return deployImpl( path, null, null, war, update, tag );
372 }
373
374 /**
375 * @param path
376 * @param war
377 * @param update
378 * @param tag
379 * @param length
380 * @return
381 * @throws TomcatManagerException
382 * @throws IOException
383 * @since 2.0
384 */
385 public TomcatManagerResponse deploy( String path, File war, boolean update, String tag, long length )
386 throws TomcatManagerException, IOException
387 {
388 return deployImpl( path, null, null, war, update, tag, length );
389 }
390
391 /**
392 * Deploys the specified context XML configuration to the specified context path.
393 *
394 * @param path the webapp context path to deploy to
395 * @param config the URL of the context XML configuration to deploy
396 * @return the Tomcat manager response
397 * @throws TomcatManagerException if the Tomcat manager request fails
398 * @throws IOException if an i/o error occurs
399 */
400 public TomcatManagerResponse deployContext( String path, URL config )
401 throws TomcatManagerException, IOException
402 {
403 return deployContext( path, config, false );
404 }
405
406 /**
407 * Deploys the specified context XML configuration to the specified context path, optionally undeploying the webapp
408 * if it already exists.
409 *
410 * @param path the webapp context path to deploy to
411 * @param config the URL of the context XML configuration to deploy
412 * @param update whether to first undeploy the webapp if it already exists
413 * @return the Tomcat manager response
414 * @throws TomcatManagerException if the Tomcat manager request fails
415 * @throws IOException if an i/o error occurs
416 */
417 public TomcatManagerResponse deployContext( String path, URL config, boolean update )
418 throws TomcatManagerException, IOException
419 {
420 return deployContext( path, config, update, null );
421 }
422
423 /**
424 * Deploys the specified context XML configuration to the specified context path, optionally undeploying the webapp
425 * if it already exists and using the specified tag name.
426 *
427 * @param path the webapp context path to deploy to
428 * @param config the URL of the context XML configuration to deploy
429 * @param update whether to first undeploy the webapp if it already exists
430 * @param tag the tag name to use
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 TomcatManagerResponse deployContext( String path, URL config, boolean update, String tag )
436 throws TomcatManagerException, IOException
437 {
438 return deployContext( path, config, null, update, tag );
439 }
440
441 /**
442 * Deploys the specified context XML configuration and WAR as a URL to the specified context path.
443 *
444 * @param path the webapp context path to deploy to
445 * @param config the URL of the context XML configuration to deploy
446 * @param war the URL of the WAR to deploy
447 * @return the Tomcat manager response
448 * @throws TomcatManagerException if the Tomcat manager request fails
449 * @throws IOException if an i/o error occurs
450 */
451 public TomcatManagerResponse deployContext( String path, URL config, URL war )
452 throws TomcatManagerException, IOException
453 {
454 return deployContext( path, config, war, false );
455 }
456
457 /**
458 * Deploys the specified context XML configuration and WAR as a URL to the specified context path, optionally
459 * undeploying the webapp if it already exists.
460 *
461 * @param path the webapp context path to deploy to
462 * @param config the URL of the context XML configuration to deploy
463 * @param war the URL of the WAR to deploy
464 * @param update whether to first undeploy the webapp if it already exists
465 * @return the Tomcat manager response
466 * @throws TomcatManagerException if the Tomcat manager request fails
467 * @throws IOException if an i/o error occurs
468 */
469 public TomcatManagerResponse deployContext( String path, URL config, URL war, boolean update )
470 throws TomcatManagerException, IOException
471 {
472 return deployContext( path, config, war, update, null );
473 }
474
475 /**
476 * Deploys the specified context XML configuration and WAR as a URL to the specified context path, optionally
477 * undeploying the webapp if it already exists and using the specified tag name.
478 *
479 * @param path the webapp context path to deploy to
480 * @param config the URL of the context XML configuration to deploy
481 * @param war the URL of the WAR to deploy
482 * @param update whether to first undeploy the webapp if it already exists
483 * @param tag the tag name to use
484 * @return the Tomcat manager response
485 * @throws TomcatManagerException if the Tomcat manager request fails
486 * @throws IOException if an i/o error occurs
487 */
488 public TomcatManagerResponse deployContext( String path, URL config, URL war, boolean update, String tag )
489 throws TomcatManagerException, IOException
490 {
491 return deployImpl( path, config, war, null, update, tag );
492 }
493
494 /**
495 * Undeploys the webapp at the specified context path.
496 *
497 * @param path the webapp context path to undeploy
498 * @return the Tomcat manager response
499 * @throws TomcatManagerException if the Tomcat manager request fails
500 * @throws IOException if an i/o error occurs
501 */
502 public TomcatManagerResponse undeploy( String path )
503 throws TomcatManagerException, IOException
504 {
505 return invoke( "/undeploy?path=" + URLEncoder.encode( path, charset ) );
506 }
507
508 /**
509 * Reloads the webapp at the specified context path.
510 *
511 * @param path the webapp context path to reload
512 * @return the Tomcat manager response
513 * @throws TomcatManagerException if the Tomcat manager request fails
514 * @throws IOException if an i/o error occurs
515 */
516 public TomcatManagerResponse reload( String path )
517 throws TomcatManagerException, IOException
518 {
519 return invoke( "/reload?path=" + URLEncoder.encode( path, charset ) );
520 }
521
522 /**
523 * Starts the webapp at the specified context path.
524 *
525 * @param path the webapp context path to start
526 * @return the Tomcat manager response
527 * @throws TomcatManagerException if the Tomcat manager request fails
528 * @throws IOException if an i/o error occurs
529 */
530 public TomcatManagerResponse start( String path )
531 throws TomcatManagerException, IOException
532 {
533 return invoke( "/start?path=" + URLEncoder.encode( path, charset ) );
534 }
535
536 /**
537 * Stops the webapp at the specified context path.
538 *
539 * @param path the webapp context path to stop
540 * @return the Tomcat manager response
541 * @throws TomcatManagerException if the Tomcat manager request fails
542 * @throws IOException if an i/o error occurs
543 */
544 public TomcatManagerResponse stop( String path )
545 throws TomcatManagerException, IOException
546 {
547 return invoke( "/stop?path=" + URLEncoder.encode( path, charset ) );
548 }
549
550 /**
551 * Lists all the currently deployed web applications.
552 *
553 * @return the list of currently deployed applications
554 * @throws TomcatManagerException if the Tomcat manager request fails
555 * @throws IOException if an i/o error occurs
556 */
557 public TomcatManagerResponse list()
558 throws TomcatManagerException, IOException
559 {
560 return invoke( "/list" );
561 }
562
563 /**
564 * Lists information about the Tomcat version, OS, and JVM properties.
565 *
566 * @return the server information
567 * @throws TomcatManagerException if the Tomcat manager request fails
568 * @throws IOException if an i/o error occurs
569 */
570 public TomcatManagerResponse getServerInfo()
571 throws TomcatManagerException, IOException
572 {
573 return invoke( "/serverinfo" );
574 }
575
576 /**
577 * Lists all of the global JNDI resources.
578 *
579 * @return the list of all global JNDI resources
580 * @throws TomcatManagerException if the Tomcat manager request fails
581 * @throws IOException if an i/o error occurs
582 */
583 public TomcatManagerResponse getResources()
584 throws TomcatManagerException, IOException
585 {
586 return getResources( null );
587 }
588
589 /**
590 * Lists the global JNDI resources of the given type.
591 *
592 * @param type the class name of the resources to list, or <code>null</code> for all
593 * @return the list of global JNDI resources of the given type
594 * @throws TomcatManagerException if the Tomcat manager request fails
595 * @throws IOException if an i/o error occurs
596 */
597 public TomcatManagerResponse getResources( String type )
598 throws TomcatManagerException, IOException
599 {
600 StringBuffer buffer = new StringBuffer();
601 buffer.append( "/resources" );
602
603 if ( type != null )
604 {
605 buffer.append( "?type=" + URLEncoder.encode( type, charset ) );
606 }
607 return invoke( buffer.toString() );
608 }
609
610 /**
611 * Lists the security role names and corresponding descriptions that are available.
612 *
613 * @return the list of security role names and corresponding descriptions
614 * @throws TomcatManagerException if the Tomcat manager request fails
615 * @throws IOException if an i/o error occurs
616 */
617 public TomcatManagerResponse getRoles()
618 throws TomcatManagerException, IOException
619 {
620 return invoke( "/roles" );
621 }
622
623 /**
624 * Lists the default session timeout and the number of currently active sessions for the given context path.
625 *
626 * @param path the context path to list session information for
627 * @return the default session timeout and the number of currently active sessions
628 * @throws TomcatManagerException if the Tomcat manager request fails
629 * @throws IOException if an i/o error occurs
630 */
631 public TomcatManagerResponse getSessions( String path )
632 throws TomcatManagerException, IOException
633 {
634 return invoke( "/sessions?path=" + URLEncoder.encode( path, charset ) );
635 }
636
637 // ----------------------------------------------------------------------
638 // Protected Methods
639 // ----------------------------------------------------------------------
640
641 /**
642 * Invokes Tomcat manager with the specified command.
643 *
644 * @param path the Tomcat manager command to invoke
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 protected TomcatManagerResponse invoke( String path )
650 throws TomcatManagerException, IOException
651 {
652 return invoke( path, null, -1 );
653 }
654
655 // ----------------------------------------------------------------------
656 // Private Methods
657 // ----------------------------------------------------------------------
658
659 private TomcatManagerResponse deployImpl( String path, URL config, URL war, File data, boolean update, String tag )
660 throws TomcatManagerException, IOException
661 {
662 return deployImpl( path, config, war, data, update, tag, -1 );
663 }
664
665 /**
666 * Deploys the specified WAR.
667 *
668 * @param path the webapp context path to deploy to
669 * @param config the URL of the context XML configuration to deploy, or null for none
670 * @param war the URL of the WAR to deploy, or null to use <code>data</code>
671 * @param data WAR file to deploy, or null to use <code>war</code>
672 * @param update whether to first undeploy the webapp if it already exists
673 * @param tag the tag name to use
674 * @return the Tomcat manager response
675 * @throws TomcatManagerException if the Tomcat manager request fails
676 * @throws IOException if an i/o error occurs
677 */
678 private TomcatManagerResponse deployImpl( String path, URL config, URL war, File data, boolean update, String tag,
679 long length )
680 throws TomcatManagerException, IOException
681 {
682 StringBuilder buffer = new StringBuilder( "/deploy" );
683 buffer.append( "?path=" ).append( URLEncoder.encode( path, charset ) );
684
685 if ( config != null )
686 {
687 buffer.append( "&config=" ).append( URLEncoder.encode( config.toString(), charset ) );
688 }
689
690 if ( war != null )
691 {
692 buffer.append( "&war=" ).append( URLEncoder.encode( war.toString(), charset ) );
693 }
694
695 if ( update )
696 {
697 buffer.append( "&update=true" );
698 }
699
700 if ( tag != null )
701 {
702 buffer.append( "&tag=" ).append( URLEncoder.encode( tag, charset ) );
703 }
704
705 return invoke( buffer.toString(), data, length );
706 }
707
708
709 /**
710 * Invokes Tomcat manager with the specified command and content data.
711 *
712 * @param path the Tomcat manager command to invoke
713 * @param data file to deploy
714 * @return the Tomcat manager response
715 * @throws TomcatManagerException if the Tomcat manager request fails
716 * @throws IOException if an i/o error occurs
717 */
718 protected TomcatManagerResponse invoke( String path, File data, long length )
719 throws TomcatManagerException, IOException
720 {
721
722 HttpRequestBase httpRequestBase = null;
723 if ( data == null )
724 {
725 httpRequestBase = new HttpGet( url + path );
726 }
727 else
728 {
729 HttpPut httpPut = new HttpPut( url + path );
730
731 httpPut.setEntity( new RequestEntityImplementation( data, length, url + path, verbose ) );
732
733 httpRequestBase = httpPut;
734
735 }
736
737 if ( userAgent != null )
738 {
739 httpRequestBase.setHeader( "User-Agent", userAgent );
740 }
741
742 HttpResponse response = httpClient.execute( httpRequestBase, localContext );
743
744 int statusCode = response.getStatusLine().getStatusCode();
745
746 switch ( statusCode )
747 {
748 // Success Codes
749 case HttpStatus.SC_OK: // 200
750 case HttpStatus.SC_CREATED: // 201
751 case HttpStatus.SC_ACCEPTED: // 202
752 break;
753 // 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"
754 case HttpStatus.SC_MOVED_PERMANENTLY: // 301
755 case HttpStatus.SC_MOVED_TEMPORARILY: // 302
756 case HttpStatus.SC_SEE_OTHER: // 303
757 String relocateUrl = calculateRelocatedUrl( response );
758 this.url = new URL( relocateUrl );
759 return invoke( path, data, length );
760 }
761
762 return new TomcatManagerResponse().setStatusCode( response.getStatusLine().getStatusCode() ).setReasonPhrase(
763 response.getStatusLine().getReasonPhrase() ).setHttpResponseBody(
764 IOUtils.toString( response.getEntity().getContent() ) );
765
766 }
767
768 protected String calculateRelocatedUrl( HttpResponse response )
769 {
770 Header locationHeader = response.getFirstHeader( "Location" );
771 String locationField = locationHeader.getValue();
772 // is it a relative Location or a full ?
773 return locationField.startsWith( "http" ) ? locationField : url.toString() + '/' + locationField;
774 }
775
776
777 /**
778 * Gets the HTTP Basic Authorization header value for the supplied username and password.
779 *
780 * @param username the username to use for authentication
781 * @param password the password to use for authentication
782 * @return the HTTP Basic Authorization header value
783 */
784 private String toAuthorization( String username, String password )
785 {
786 StringBuffer buffer = new StringBuffer();
787 buffer.append( username ).append( ':' );
788 if ( password != null )
789 {
790 buffer.append( password );
791 }
792 return "Basic " + new String( Base64.encodeBase64( buffer.toString().getBytes() ) );
793 }
794
795 private final class RequestEntityImplementation
796 extends AbstractHttpEntity
797 {
798
799 private final static int BUFFER_SIZE = 2048;
800
801 private File file;
802
803 PrintStream out = System.out;
804
805 private long length = -1;
806
807 private int lastLength;
808
809 private String url;
810
811 private long startTime;
812
813 private boolean verbose;
814
815 private RequestEntityImplementation( final File file, long length, String url, boolean verbose )
816 {
817 this.file = file;
818 this.length = length;
819 this.url = url;
820 this.verbose = verbose;
821 }
822
823 public long getContentLength()
824 {
825 return length >= 0 ? length : ( file.length() >= 0 ? file.length() : -1 );
826 }
827
828
829 public InputStream getContent()
830 throws IOException, IllegalStateException
831 {
832 return new FileInputStream( this.file );
833 }
834
835 public boolean isRepeatable()
836 {
837 return true;
838 }
839
840
841 public void writeTo( final OutputStream outstream )
842 throws IOException
843 {
844 long completed = 0;
845 if ( outstream == null )
846 {
847 throw new IllegalArgumentException( "Output stream may not be null" );
848 }
849 FileInputStream stream = new FileInputStream( this.file );
850 transferInitiated( this.url );
851 this.startTime = System.currentTimeMillis();
852 try
853 {
854 byte[] buffer = new byte[BUFFER_SIZE];
855
856 int l;
857 if ( this.length < 0 )
858 {
859 // until EOF
860 while ( ( l = stream.read( buffer ) ) != -1 )
861 {
862 transferProgressed( completed += buffer.length, -1 );
863 outstream.write( buffer, 0, l );
864 }
865 }
866 else
867 {
868 // no need to consume more than length
869 long remaining = this.length;
870 while ( remaining > 0 )
871 {
872 int transferSize = (int) Math.min( BUFFER_SIZE, remaining );
873 completed += transferSize;
874 l = stream.read( buffer, 0, transferSize );
875 if ( l == -1 )
876 {
877 break;
878 }
879
880 outstream.write( buffer, 0, l );
881 remaining -= l;
882 transferProgressed( completed, this.length );
883 }
884 }
885 transferSucceeded( completed );
886 }
887 finally
888 {
889 stream.close();
890 out.println();
891 }
892 // end transfer
893 }
894
895 public boolean isStreaming()
896 {
897 return true;
898 }
899
900
901 public void transferInitiated( String url )
902 {
903 String message = "Uploading";
904
905 out.println( message + ": " + url );
906 }
907
908 public void transferProgressed( long completedSize, long totalSize )
909 {
910 if ( !verbose )
911 {
912 return;
913 }
914
915 StringBuilder buffer = new StringBuilder( 64 );
916
917 buffer.append( getStatus( completedSize, totalSize ) ).append( " " );
918 lastLength = buffer.length();
919 buffer.append( '\r' );
920
921 out.print( buffer );
922 }
923
924 public void transferSucceeded( long contentLength )
925 {
926
927 if ( contentLength >= 0 )
928 {
929 String type = "Uploaded";
930 String len = contentLength >= 1024 ? toKB( contentLength ) + " KB" : contentLength + " B";
931
932 String throughput = "";
933 long duration = System.currentTimeMillis() - startTime;
934 if ( duration > 0 )
935 {
936 DecimalFormat format = new DecimalFormat( "0.0", new DecimalFormatSymbols( Locale.ENGLISH ) );
937 double kbPerSec = ( contentLength / 1024.0 ) / ( duration / 1000.0 );
938 throughput = " at " + format.format( kbPerSec ) + " KB/sec";
939 }
940
941 out.println( type + ": " + url + " (" + len + throughput + ")" );
942 }
943 }
944
945 private String getStatus( long complete, long total )
946 {
947 if ( total >= 1024 )
948 {
949 return toKB( complete ) + "/" + toKB( total ) + " KB ";
950 }
951 else if ( total >= 0 )
952 {
953 return complete + "/" + total + " B ";
954 }
955 else if ( complete >= 1024 )
956 {
957 return toKB( complete ) + " KB ";
958 }
959 else
960 {
961 return complete + " B ";
962 }
963 }
964
965 private long toKB( long bytes )
966 {
967 return ( bytes + 1023 ) / 1024;
968 }
969
970 }
971 }