1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.manager;
20
21
22 import java.io.BufferedOutputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import java.util.Iterator;
29
30 import javax.management.MBeanServer;
31 import javax.management.ObjectName;
32 import javax.naming.Binding;
33 import javax.naming.InitialContext;
34 import javax.naming.NamingEnumeration;
35 import javax.naming.NamingException;
36 import javax.servlet.ServletException;
37 import javax.servlet.ServletInputStream;
38 import javax.servlet.UnavailableException;
39 import javax.servlet.http.HttpServlet;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse;
42
43 import org.apache.catalina.Container;
44 import org.apache.catalina.ContainerServlet;
45 import org.apache.catalina.Context;
46 import org.apache.catalina.Engine;
47 import org.apache.catalina.Globals;
48 import org.apache.catalina.Host;
49 import org.apache.catalina.Lifecycle;
50 import org.apache.catalina.Manager;
51 import org.apache.catalina.Role;
52 import org.apache.catalina.Server;
53 import org.apache.catalina.ServerFactory;
54 import org.apache.catalina.Session;
55 import org.apache.catalina.UserDatabase;
56 import org.apache.catalina.Wrapper;
57 import org.apache.catalina.core.StandardHost;
58 import org.apache.catalina.core.StandardServer;
59 import org.apache.catalina.util.RequestUtil;
60 import org.apache.catalina.util.ServerInfo;
61 import org.apache.catalina.util.StringManager;
62 import org.apache.tomcat.util.modeler.Registry;
63
64
65 /**
66 * Servlet that enables remote management of the web applications installed
67 * within the same virtual host as this web application is. Normally, this
68 * functionality will be protected by a security constraint in the web
69 * application deployment descriptor. However, this requirement can be
70 * relaxed during testing.
71 * <p>
72 * This servlet examines the value returned by <code>getPathInfo()</code>
73 * and related query parameters to determine what action is being requested.
74 * The following actions and parameters (starting after the servlet path)
75 * are supported:
76 * <ul>
77 * <li><b>/deploy?config={config-url}</b> - Install and start a new
78 * web application, based on the contents of the context configuration
79 * file found at the specified URL. The <code>docBase</code> attribute
80 * of the context configuration file is used to locate the actual
81 * WAR or directory containing the application.</li>
82 * <li><b>/deploy?config={config-url}&war={war-url}/</b> - Install and start
83 * a new web application, based on the contents of the context
84 * configuration file found at <code>{config-url}</code>, overriding the
85 * <code>docBase</code> attribute with the contents of the web
86 * application archive found at <code>{war-url}</code>.</li>
87 * <li><b>/deploy?path=/xxx&war={war-url}</b> - Install and start a new
88 * web application attached to context path <code>/xxx</code>, based
89 * on the contents of the web application archive found at the
90 * specified URL.</li>
91 * <li><b>/list</b> - List the context paths of all currently installed web
92 * applications for this virtual host. Each context will be listed with
93 * the following format <code>path:status:sessions</code>.
94 * Where path is the context path. Status is either running or stopped.
95 * Sessions is the number of active Sessions.</li>
96 * <li><b>/reload?path=/xxx</b> - Reload the Java classes and resources for
97 * the application at the specified path.</li>
98 * <li><b>/resources?type=xxxx</b> - Enumerate the available global JNDI
99 * resources, optionally limited to those of the specified type
100 * (fully qualified Java class name), if available.</li>
101 * <li><b>/roles</b> - Enumerate the available security role names and
102 * descriptions from the user database connected to the <code>users</code>
103 * resource reference.
104 * <li><b>/serverinfo</b> - Display system OS and JVM properties.
105 * <li><b>/sessions</b> - Deprecated. Use expire.
106 * <li><b>/expire?path=/xxx</b> - List session idle timeinformation about the
107 * web application attached to context path <code>/xxx</code> for this
108 * virtual host.</li>
109 * <li><b>/expire?path=/xxx&idle=mm</b> - Expire sessions
110 * for the context path <code>/xxx</code> which were idle for at
111 * least mm minutes.</li>
112 * <li><b>/start?path=/xxx</b> - Start the web application attached to
113 * context path <code>/xxx</code> for this virtual host.</li>
114 * <li><b>/stop?path=/xxx</b> - Stop the web application attached to
115 * context path <code>/xxx</code> for this virtual host.</li>
116 * <li><b>/undeploy?path=/xxx</b> - Shutdown and remove the web application
117 * attached to context path <code>/xxx</code> for this virtual host,
118 * and remove the underlying WAR file or document base directory.
119 * (<em>NOTE</em> - This is only allowed if the WAR file or document
120 * base is stored in the <code>appBase</code> directory of this host,
121 * typically as a result of being placed there via the <code>/deploy</code>
122 * command.</li>
123 * </ul>
124 * <p>Use <code>path=/</code> for the ROOT context.</p>
125 * <p>The syntax of the URL for a web application archive must conform to one
126 * of the following patterns to be successfully deployed:</p>
127 * <ul>
128 * <li><b>file:/absolute/path/to/a/directory</b> - You can specify the absolute
129 * path of a directory that contains the unpacked version of a web
130 * application. This directory will be attached to the context path you
131 * specify without any changes.</li>
132 * <li><b>jar:file:/absolute/path/to/a/warfile.war!/</b> - You can specify a
133 * URL to a local web application archive file. The syntax must conform to
134 * the rules specified by the <code>JarURLConnection</code> class for a
135 * reference to an entire JAR file.</li>
136 * <li><b>jar:http://hostname:port/path/to/a/warfile.war!/</b> - You can specify
137 * a URL to a remote (HTTP-accessible) web application archive file. The
138 * syntax must conform to the rules specified by the
139 * <code>JarURLConnection</code> class for a reference to an entire
140 * JAR file.</li>
141 * </ul>
142 * <p>
143 * <b>NOTE</b> - Attempting to reload or remove the application containing
144 * this servlet itself will not succeed. Therefore, this servlet should
145 * generally be deployed as a separate web application within the virtual host
146 * to be managed.
147 * <p>
148 * <b>NOTE</b> - For security reasons, this application will not operate
149 * when accessed via the invoker servlet. You must explicitly map this servlet
150 * with a servlet mapping, and you will always want to protect it with
151 * appropriate security constraints as well.
152 * <p>
153 * The following servlet initialization parameters are recognized:
154 * <ul>
155 * <li><b>debug</b> - The debugging detail level that controls the amount
156 * of information that is logged by this servlet. Default is zero.
157 * </ul>
158 *
159 * @author Craig R. McClanahan
160 * @author Remy Maucherat
161 * @version $Revision: 915603 $ $Date: 2010-02-24 01:07:06 +0100 (Wed, 24 Feb 2010) $
162 */
163
164 public class ManagerServlet
165 extends HttpServlet implements ContainerServlet {
166
167
168 // ----------------------------------------------------- Instance Variables
169
170
171 /**
172 * Path where context descriptors should be deployed.
173 */
174 protected File configBase = null;
175
176
177 /**
178 * The Context container associated with our web application.
179 */
180 protected Context context = null;
181
182
183 /**
184 * The debugging detail level for this servlet.
185 */
186 protected int debug = 1;
187
188
189 /**
190 * File object representing the directory into which the deploy() command
191 * will store the WAR and context configuration files that have been
192 * uploaded.
193 */
194 protected File deployed = null;
195
196
197 /**
198 * Path used to store revisions of webapps.
199 */
200 protected File versioned = null;
201
202
203 /**
204 * Path used to store context descriptors.
205 */
206 protected File contextDescriptors = null;
207
208
209 /**
210 * The associated host.
211 */
212 protected Host host = null;
213
214
215 /**
216 * The host appBase.
217 */
218 protected File appBase = null;
219
220
221 /**
222 * MBean server.
223 */
224 protected MBeanServer mBeanServer = null;
225
226
227 /**
228 * The associated deployer ObjectName.
229 */
230 protected ObjectName oname = null;
231
232
233 /**
234 * The global JNDI <code>NamingContext</code> for this server,
235 * if available.
236 */
237 protected javax.naming.Context global = null;
238
239
240 /**
241 * The string manager for this package.
242 */
243 protected static StringManager sm =
244 StringManager.getManager(Constants.Package);
245
246
247 /**
248 * The Wrapper container associated with this servlet.
249 */
250 protected Wrapper wrapper = null;
251
252
253 // ----------------------------------------------- ContainerServlet Methods
254
255
256 /**
257 * Return the Wrapper with which we are associated.
258 */
259 public Wrapper getWrapper() {
260
261 return (this.wrapper);
262
263 }
264
265
266 /**
267 * Set the Wrapper with which we are associated.
268 *
269 * @param wrapper The new wrapper
270 */
271 public void setWrapper(Wrapper wrapper) {
272
273 this.wrapper = wrapper;
274 if (wrapper == null) {
275 context = null;
276 host = null;
277 oname = null;
278 } else {
279 context = (Context) wrapper.getParent();
280 host = (Host) context.getParent();
281 Engine engine = (Engine) host.getParent();
282 try {
283 oname = new ObjectName(engine.getName()
284 + ":type=Deployer,host=" + host.getName());
285 } catch (Exception e) {
286 // ?
287 }
288 }
289
290 // Retrieve the MBean server
291 mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
292
293 }
294
295
296 // --------------------------------------------------------- Public Methods
297
298
299 /**
300 * Finalize this servlet.
301 */
302 public void destroy() {
303
304 ; // No actions necessary
305
306 }
307
308
309 /**
310 * Process a GET request for the specified resource.
311 *
312 * @param request The servlet request we are processing
313 * @param response The servlet response we are creating
314 *
315 * @exception IOException if an input/output error occurs
316 * @exception ServletException if a servlet-specified error occurs
317 */
318 public void doGet(HttpServletRequest request,
319 HttpServletResponse response)
320 throws IOException, ServletException {
321
322 // Verify that we were not accessed using the invoker servlet
323 if (request.getAttribute(Globals.INVOKED_ATTR) != null)
324 throw new UnavailableException
325 (sm.getString("managerServlet.cannotInvoke"));
326
327 // Identify the request parameters that we need
328 String command = request.getPathInfo();
329 if (command == null)
330 command = request.getServletPath();
331 String config = request.getParameter("config");
332 String path = request.getParameter("path");
333 String type = request.getParameter("type");
334 String war = request.getParameter("war");
335 String tag = request.getParameter("tag");
336 boolean update = false;
337 if ((request.getParameter("update") != null)
338 && (request.getParameter("update").equals("true"))) {
339 update = true;
340 }
341
342 // Prepare our output writer to generate the response message
343 response.setContentType("text/plain; charset=" + Constants.CHARSET);
344 PrintWriter writer = response.getWriter();
345
346 // Process the requested command (note - "/deploy" is not listed here)
347 if (command == null) {
348 writer.println(sm.getString("managerServlet.noCommand"));
349 } else if (command.equals("/deploy")) {
350 if (war != null || config != null) {
351 deploy(writer, config, path, war, update);
352 } else {
353 deploy(writer, path, tag);
354 }
355 } else if (command.equals("/install")) {
356 // Deprecated
357 deploy(writer, config, path, war, false);
358 } else if (command.equals("/list")) {
359 list(writer);
360 } else if (command.equals("/reload")) {
361 reload(writer, path);
362 } else if (command.equals("/remove")) {
363 // Deprecated
364 undeploy(writer, path);
365 } else if (command.equals("/resources")) {
366 resources(writer, type);
367 } else if (command.equals("/roles")) {
368 roles(writer);
369 } else if (command.equals("/save")) {
370 save(writer, path);
371 } else if (command.equals("/serverinfo")) {
372 serverinfo(writer);
373 } else if (command.equals("/sessions")) {
374 expireSessions(writer, path, request);
375 } else if (command.equals("/expire")) {
376 expireSessions(writer, path, request);
377 } else if (command.equals("/start")) {
378 start(writer, path);
379 } else if (command.equals("/stop")) {
380 stop(writer, path);
381 } else if (command.equals("/undeploy")) {
382 undeploy(writer, path);
383 } else if (command.equals("/findleaks")) {
384 findleaks(writer);
385 } else {
386 writer.println(sm.getString("managerServlet.unknownCommand",
387 command));
388 }
389
390 // Finish up the response
391 writer.flush();
392 writer.close();
393
394 }
395
396
397 /**
398 * Process a PUT request for the specified resource.
399 *
400 * @param request The servlet request we are processing
401 * @param response The servlet response we are creating
402 *
403 * @exception IOException if an input/output error occurs
404 * @exception ServletException if a servlet-specified error occurs
405 */
406 public void doPut(HttpServletRequest request,
407 HttpServletResponse response)
408 throws IOException, ServletException {
409
410 // Verify that we were not accessed using the invoker servlet
411 if (request.getAttribute(Globals.INVOKED_ATTR) != null)
412 throw new UnavailableException
413 (sm.getString("managerServlet.cannotInvoke"));
414
415 // Identify the request parameters that we need
416 String command = request.getPathInfo();
417 if (command == null)
418 command = request.getServletPath();
419 String path = request.getParameter("path");
420 String tag = request.getParameter("tag");
421 boolean update = false;
422 if ((request.getParameter("update") != null)
423 && (request.getParameter("update").equals("true"))) {
424 update = true;
425 }
426
427 // Prepare our output writer to generate the response message
428 response.setContentType("text/plain;charset="+Constants.CHARSET);
429 PrintWriter writer = response.getWriter();
430
431 // Process the requested command
432 if (command == null) {
433 writer.println(sm.getString("managerServlet.noCommand"));
434 } else if (command.equals("/deploy")) {
435 deploy(writer, path, tag, update, request);
436 } else {
437 writer.println(sm.getString("managerServlet.unknownCommand",
438 command));
439 }
440
441 // Finish up the response
442 writer.flush();
443 writer.close();
444
445 }
446
447
448 /**
449 * Initialize this servlet.
450 */
451 public void init() throws ServletException {
452
453 // Ensure that our ContainerServlet properties have been set
454 if ((wrapper == null) || (context == null))
455 throw new UnavailableException
456 (sm.getString("managerServlet.noWrapper"));
457
458 // Verify that we were not accessed using the invoker servlet
459 String servletName = getServletConfig().getServletName();
460 if (servletName == null)
461 servletName = "";
462 if (servletName.startsWith("org.apache.catalina.INVOKER."))
463 throw new UnavailableException
464 (sm.getString("managerServlet.cannotInvoke"));
465
466 // Set our properties from the initialization parameters
467 String value = null;
468 try {
469 value = getServletConfig().getInitParameter("debug");
470 debug = Integer.parseInt(value);
471 } catch (Throwable t) {
472 ;
473 }
474
475 // Acquire global JNDI resources if available
476 Server server = ServerFactory.getServer();
477 if ((server != null) && (server instanceof StandardServer)) {
478 global = ((StandardServer) server).getGlobalNamingContext();
479 }
480
481 // Calculate the directory into which we will be deploying applications
482 versioned = (File) getServletContext().getAttribute
483 ("javax.servlet.context.tempdir");
484
485 // Identify the appBase of the owning Host of this Context
486 // (if any)
487 String appBase = ((Host) context.getParent()).getAppBase();
488 deployed = new File(appBase);
489 if (!deployed.isAbsolute()) {
490 deployed = new File(System.getProperty("catalina.base"),
491 appBase);
492 }
493 configBase = new File(System.getProperty("catalina.base"), "conf");
494 Container container = context;
495 Container host = null;
496 Container engine = null;
497 while (container != null) {
498 if (container instanceof Host)
499 host = container;
500 if (container instanceof Engine)
501 engine = container;
502 container = container.getParent();
503 }
504 if (engine != null) {
505 configBase = new File(configBase, engine.getName());
506 }
507 if (host != null) {
508 configBase = new File(configBase, host.getName());
509 }
510 // Note: The directory must exist for this to work.
511
512 // Log debugging messages as necessary
513 if (debug >= 1) {
514 log("init: Associated with Deployer '" +
515 oname + "'");
516 if (global != null) {
517 log("init: Global resources are available");
518 }
519 }
520
521 }
522
523
524
525 // -------------------------------------------------------- Private Methods
526
527
528 /**
529 * Find potential memory leaks caused by web application reload.
530 */
531 protected void findleaks(PrintWriter writer) {
532
533 if (!(host instanceof StandardHost)) {
534 writer.println(sm.getString("managerServlet.findleaksFail"));
535 return;
536 }
537
538 String[] results =
539 ((StandardHost) host).findReloadedContextMemoryLeaks();
540
541 for (String result : results) {
542 if ("".equals(result)) {
543 result = "/";
544 }
545 writer.println(result);
546 }
547 }
548
549
550 /**
551 * Store server configuration.
552 *
553 * @param path Optional context path to save
554 */
555 protected synchronized void save(PrintWriter writer, String path) {
556
557 Server server = ServerFactory.getServer();
558
559 if (!(server instanceof StandardServer)) {
560 writer.println(sm.getString("managerServlet.saveFail", server));
561 return;
562 }
563
564 if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
565 try {
566 ((StandardServer) server).storeConfig();
567 writer.println(sm.getString("managerServlet.saved"));
568 } catch (Exception e) {
569 log("managerServlet.storeConfig", e);
570 writer.println(sm.getString("managerServlet.exception",
571 e.toString()));
572 return;
573 }
574 } else {
575 String contextPath = path;
576 if (path.equals("/")) {
577 contextPath = "";
578 }
579 Context context = (Context) host.findChild(contextPath);
580 if (context == null) {
581 writer.println(sm.getString("managerServlet.noContext", path));
582 return;
583 }
584 try {
585 ((StandardServer) server).storeContext(context);
586 writer.println(sm.getString("managerServlet.savedContext",
587 path));
588 } catch (Exception e) {
589 log("managerServlet.save[" + path + "]", e);
590 writer.println(sm.getString("managerServlet.exception",
591 e.toString()));
592 return;
593 }
594 }
595
596 }
597
598
599 /**
600 * Deploy a web application archive (included in the current request)
601 * at the specified context path.
602 *
603 * @param writer Writer to render results to
604 * @param path Context path of the application to be installed
605 * @param tag Tag to be associated with the webapp
606 * @param request Servlet request we are processing
607 */
608 protected synchronized void deploy
609 (PrintWriter writer, String path,
610 String tag, boolean update, HttpServletRequest request) {
611
612 if (debug >= 1) {
613 log("deploy: Deploying web application at '" + path + "'");
614 }
615
616 // Validate the requested context path
617 if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
618 writer.println(sm.getString("managerServlet.invalidPath", path));
619 return;
620 }
621 String displayPath = path;
622 if( path.equals("/") )
623 path = "";
624 String basename = getDocBase(path);
625
626 // Check if app already exists, or undeploy it if updating
627 Context context = (Context) host.findChild(path);
628 if (update) {
629 if (context != null) {
630 undeploy(writer, displayPath);
631 }
632 context = (Context) host.findChild(path);
633 }
634 if (context != null) {
635 writer.println
636 (sm.getString("managerServlet.alreadyContext",
637 displayPath));
638 return;
639 }
640
641 // Calculate the base path
642 File deployedPath = deployed;
643 if (tag != null) {
644 deployedPath = new File(versioned, tag);
645 deployedPath.mkdirs();
646 }
647
648 // Upload the web application archive to a local WAR file
649 File localWar = new File(deployedPath, basename + ".war");
650 if (debug >= 2) {
651 log("Uploading WAR file to " + localWar);
652 }
653
654 // Copy WAR to appBase
655 try {
656 if (!isServiced(path)) {
657 addServiced(path);
658 try {
659 // Upload WAR
660 uploadWar(request, localWar);
661 // Copy WAR and XML to the host app base if needed
662 if (tag != null) {
663 deployedPath = deployed;
664 File localWarCopy = new File(deployedPath, basename + ".war");
665 copy(localWar, localWarCopy);
666 localWar = localWarCopy;
667 copy(localWar, new File(getAppBase(), basename + ".war"));
668 }
669 // Perform new deployment
670 check(path);
671 } finally {
672 removeServiced(path);
673 }
674 }
675 } catch (Exception e) {
676 log("managerServlet.check[" + displayPath + "]", e);
677 writer.println(sm.getString("managerServlet.exception",
678 e.toString()));
679 return;
680 }
681
682 context = (Context) host.findChild(path);
683 if (context != null && context.getConfigured()) {
684 writer.println(sm.getString("managerServlet.deployed", displayPath));
685 } else {
686 // Something failed
687 writer.println(sm.getString("managerServlet.deployFailed", displayPath));
688 }
689
690 }
691
692
693 /**
694 * Install an application for the specified path from the specified
695 * web application archive.
696 *
697 * @param writer Writer to render results to
698 * @param tag Revision tag to deploy from
699 * @param path Context path of the application to be installed
700 */
701 protected void deploy(PrintWriter writer, String path, String tag) {
702
703 // Validate the requested context path
704 if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
705 writer.println(sm.getString("managerServlet.invalidPath", path));
706 return;
707 }
708 String displayPath = path;
709 if( path.equals("/") )
710 path = "";
711
712 // Calculate the base path
713 File deployedPath = versioned;
714 if (tag != null) {
715 deployedPath = new File(deployedPath, tag);
716 }
717
718 // Find the local WAR file
719 File localWar = new File(deployedPath, getDocBase(path) + ".war");
720 // Find the local context deployment file (if any)
721 File localXml = new File(configBase, getConfigFile(path) + ".xml");
722
723 // Check if app already exists, or undeploy it if updating
724 Context context = (Context) host.findChild(path);
725 if (context != null) {
726 undeploy(writer, displayPath);
727 }
728
729 // Copy WAR to appBase
730 try {
731 if (!isServiced(path)) {
732 addServiced(path);
733 try {
734 copy(localWar, new File(getAppBase(), getDocBase(path) + ".war"));
735 // Perform new deployment
736 check(path);
737 } finally {
738 removeServiced(path);
739 }
740 }
741 } catch (Exception e) {
742 log("managerServlet.check[" + displayPath + "]", e);
743 writer.println(sm.getString("managerServlet.exception",
744 e.toString()));
745 return;
746 }
747
748 context = (Context) host.findChild(path);
749 if (context != null && context.getConfigured()) {
750 writer.println(sm.getString("managerServlet.deployed", displayPath));
751 } else {
752 // Something failed
753 writer.println(sm.getString("managerServlet.deployFailed", displayPath));
754 }
755
756 }
757
758
759 /**
760 * Install an application for the specified path from the specified
761 * web application archive.
762 *
763 * @param writer Writer to render results to
764 * @param config URL of the context configuration file to be installed
765 * @param path Context path of the application to be installed
766 * @param war URL of the web application archive to be installed
767 * @param update true to override any existing webapp on the path
768 */
769 protected void deploy(PrintWriter writer, String config,
770 String path, String war, boolean update) {
771
772 if (config != null && config.length() == 0) {
773 config = null;
774 }
775 if (war != null && war.length() == 0) {
776 war = null;
777 }
778
779 if (debug >= 1) {
780 if (config != null && config.length() > 0) {
781 if (war != null) {
782 log("install: Installing context configuration at '" +
783 config + "' from '" + war + "'");
784 } else {
785 log("install: Installing context configuration at '" +
786 config + "'");
787 }
788 } else {
789 if (path != null && path.length() > 0) {
790 log("install: Installing web application at '" + path +
791 "' from '" + war + "'");
792 } else {
793 log("install: Installing web application from '" + war + "'");
794 }
795 }
796 }
797
798 if (path == null || path.length() == 0 || !path.startsWith("/")) {
799 writer.println(sm.getString("managerServlet.invalidPath",
800 RequestUtil.filter(path)));
801 return;
802 }
803 String displayPath = path;
804 if("/".equals(path)) {
805 path = "";
806 }
807
808 // Check if app already exists, or undeploy it if updating
809 Context context = (Context) host.findChild(path);
810 if (update) {
811 if (context != null) {
812 undeploy(writer, displayPath);
813 }
814 context = (Context) host.findChild(path);
815 }
816 if (context != null) {
817 writer.println
818 (sm.getString("managerServlet.alreadyContext",
819 displayPath));
820 return;
821 }
822
823 if (config != null && (config.startsWith("file:"))) {
824 config = config.substring("file:".length());
825 }
826 if (war != null && (war.startsWith("file:"))) {
827 war = war.substring("file:".length());
828 }
829
830 try {
831 if (!isServiced(path)) {
832 addServiced(path);
833 try {
834 if (config != null) {
835 configBase.mkdirs();
836 copy(new File(config),
837 new File(configBase, getConfigFile(path) + ".xml"));
838 }
839 if (war != null) {
840 if (war.endsWith(".war")) {
841 copy(new File(war),
842 new File(getAppBase(), getDocBase(path) + ".war"));
843 } else {
844 copy(new File(war),
845 new File(getAppBase(), getDocBase(path)));
846 }
847 }
848 // Perform new deployment
849 check(path);
850 } finally {
851 removeServiced(path);
852 }
853 }
854 context = (Context) host.findChild(path);
855 if (context != null && context.getConfigured() && context.getAvailable()) {
856 writer.println(sm.getString("managerServlet.deployed", displayPath));
857 } else if (context!=null && !context.getAvailable()) {
858 writer.println(sm.getString("managerServlet.deployedButNotStarted", displayPath));
859 } else {
860 // Something failed
861 writer.println(sm.getString("managerServlet.deployFailed", displayPath));
862 }
863 } catch (Throwable t) {
864 log("ManagerServlet.install[" + displayPath + "]", t);
865 writer.println(sm.getString("managerServlet.exception",
866 t.toString()));
867 }
868
869 }
870
871
872 /**
873 * Render a list of the currently active Contexts in our virtual host.
874 *
875 * @param writer Writer to render to
876 */
877 protected void list(PrintWriter writer) {
878
879 if (debug >= 1)
880 log("list: Listing contexts for virtual host '" +
881 host.getName() + "'");
882
883 writer.println(sm.getString("managerServlet.listed",
884 host.getName()));
885 Container[] contexts = host.findChildren();
886 for (int i = 0; i < contexts.length; i++) {
887 Context context = (Context) contexts[i];
888 String displayPath = context.getPath();
889 if( displayPath.equals("") )
890 displayPath = "/";
891 if (context != null ) {
892 if (context.getAvailable()) {
893 writer.println(sm.getString("managerServlet.listitem",
894 displayPath,
895 "running",
896 "" + context.getManager().findSessions().length,
897 context.getDocBase()));
898 } else {
899 writer.println(sm.getString("managerServlet.listitem",
900 displayPath,
901 "stopped",
902 "0",
903 context.getDocBase()));
904 }
905 }
906 }
907 }
908
909
910 /**
911 * Reload the web application at the specified context path.
912 *
913 * @param writer Writer to render to
914 * @param path Context path of the application to be restarted
915 */
916 protected void reload(PrintWriter writer, String path) {
917
918 if (debug >= 1)
919 log("restart: Reloading web application at '" + path + "'");
920
921 if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
922 writer.println(sm.getString("managerServlet.invalidPath",
923 RequestUtil.filter(path)));
924 return;
925 }
926 String displayPath = path;
927 if( path.equals("/") )
928 path = "";
929
930 try {
931 Context context = (Context) host.findChild(path);
932 if (context == null) {
933 writer.println(sm.getString
934 ("managerServlet.noContext",
935 RequestUtil.filter(displayPath)));
936 return;
937 }
938 // It isn't possible for the manager to reload itself
939 if (context.getPath().equals(this.context.getPath())) {
940 writer.println(sm.getString("managerServlet.noSelf"));
941 return;
942 }
943 context.reload();
944 writer.println
945 (sm.getString("managerServlet.reloaded", displayPath));
946 } catch (Throwable t) {
947 log("ManagerServlet.reload[" + displayPath + "]", t);
948 writer.println(sm.getString("managerServlet.exception",
949 t.toString()));
950 }
951
952 }
953
954
955 /**
956 * Render a list of available global JNDI resources.
957 *
958 * @param type Fully qualified class name of the resource type of interest,
959 * or <code>null</code> to list resources of all types
960 */
961 protected void resources(PrintWriter writer, String type) {
962
963 if (debug >= 1) {
964 if (type != null) {
965 log("resources: Listing resources of type " + type);
966 } else {
967 log("resources: Listing resources of all types");
968 }
969 }
970
971 // Is the global JNDI resources context available?
972 if (global == null) {
973 writer.println(sm.getString("managerServlet.noGlobal"));
974 return;
975 }
976
977 // Enumerate the global JNDI resources of the requested type
978 if (type != null) {
979 writer.println(sm.getString("managerServlet.resourcesType",
980 type));
981 } else {
982 writer.println(sm.getString("managerServlet.resourcesAll"));
983 }
984
985 Class clazz = null;
986 try {
987 if (type != null) {
988 clazz = Class.forName(type);
989 }
990 } catch (Throwable t) {
991 log("ManagerServlet.resources[" + type + "]", t);
992 writer.println(sm.getString("managerServlet.exception",
993 t.toString()));
994 return;
995 }
996
997 printResources(writer, "", global, type, clazz);
998
999 }
1000
1001
1002 /**
1003 * List the resources of the given context.
1004 */
1005 protected void printResources(PrintWriter writer, String prefix,
1006 javax.naming.Context namingContext,
1007 String type, Class clazz) {
1008
1009 try {
1010 NamingEnumeration items = namingContext.listBindings("");
1011 while (items.hasMore()) {
1012 Binding item = (Binding) items.next();
1013 if (item.getObject() instanceof javax.naming.Context) {
1014 printResources
1015 (writer, prefix + item.getName() + "/",
1016 (javax.naming.Context) item.getObject(), type, clazz);
1017 } else {
1018 if ((clazz != null) &&
1019 (!(clazz.isInstance(item.getObject())))) {
1020 continue;
1021 }
1022 writer.print(prefix + item.getName());
1023 writer.print(':');
1024 writer.print(item.getClassName());
1025 // Do we want a description if available?
1026 writer.println();
1027 }
1028 }
1029 } catch (Throwable t) {
1030 log("ManagerServlet.resources[" + type + "]", t);
1031 writer.println(sm.getString("managerServlet.exception",
1032 t.toString()));
1033 }
1034
1035 }
1036
1037
1038 /**
1039 * Render a list of security role names (and corresponding descriptions)
1040 * from the <code>org.apache.catalina.UserDatabase</code> resource that is
1041 * connected to the <code>users</code> resource reference. Typically, this
1042 * will be the global user database, but can be adjusted if you have
1043 * different user databases for different virtual hosts.
1044 *
1045 * @param writer Writer to render to
1046 */
1047 protected void roles(PrintWriter writer) {
1048
1049 if (debug >= 1) {
1050 log("roles: List security roles from user database");
1051 }
1052
1053 // Look up the UserDatabase instance we should use
1054 UserDatabase database = null;
1055 try {
1056 InitialContext ic = new InitialContext();
1057 database = (UserDatabase) ic.lookup("java:comp/env/users");
1058 } catch (NamingException e) {
1059 writer.println(sm.getString("managerServlet.userDatabaseError"));
1060 log("java:comp/env/users", e);
1061 return;
1062 }
1063 if (database == null) {
1064 writer.println(sm.getString("managerServlet.userDatabaseMissing"));
1065 return;
1066 }
1067
1068 // Enumerate the available roles
1069 writer.println(sm.getString("managerServlet.rolesList"));
1070 Iterator roles = database.getRoles();
1071 if (roles != null) {
1072 while (roles.hasNext()) {
1073 Role role = (Role) roles.next();
1074 writer.print(role.getRolename());
1075 writer.print(':');
1076 if (role.getDescription() != null) {
1077 writer.print(role.getDescription());
1078 }
1079 writer.println();
1080 }
1081 }
1082
1083
1084 }
1085
1086
1087 /**
1088 * Writes System OS and JVM properties.
1089 * @param writer Writer to render to
1090 */
1091 protected void serverinfo(PrintWriter writer) {
1092 if (debug >= 1)
1093 log("serverinfo");
1094 try {
1095 StringBuffer props = new StringBuffer();
1096 props.append("OK - Server info");
1097 props.append("\nTomcat Version: ");
1098 props.append(ServerInfo.getServerInfo());
1099 props.append("\nOS Name: ");
1100 props.append(System.getProperty("os.name"));
1101 props.append("\nOS Version: ");
1102 props.append(System.getProperty("os.version"));
1103 props.append("\nOS Architecture: ");
1104 props.append(System.getProperty("os.arch"));
1105 props.append("\nJVM Version: ");
1106 props.append(System.getProperty("java.runtime.version"));
1107 props.append("\nJVM Vendor: ");
1108 props.append(System.getProperty("java.vm.vendor"));
1109 writer.println(props.toString());
1110 } catch (Throwable t) {
1111 getServletContext().log("ManagerServlet.serverinfo",t);
1112 writer.println(sm.getString("managerServlet.exception",
1113 t.toString()));
1114 }
1115 }
1116
1117 /**
1118 * Session information for the web application at the specified context path.
1119 * Displays a profile of session lastAccessedTime listing number
1120 * of sessions for each 10 minute interval up to 10 hours.
1121 *
1122 * @param writer Writer to render to
1123 * @param path Context path of the application to list session information for
1124 * @param idle Expire all sessions with idle time ≥ idle for this context
1125 */
1126 protected void sessions(PrintWriter writer, String path, int idle) {
1127
1128 if (debug >= 1) {
1129 log("sessions: Session information for web application at '" + path + "'");
1130 if (idle >= 0)
1131 log("sessions: Session expiration for " + idle + " minutes '" + path + "'");
1132 }
1133
1134 if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1135 writer.println(sm.getString("managerServlet.invalidPath",
1136 RequestUtil.filter(path)));
1137 return;
1138 }
1139 String displayPath = path;
1140 if( path.equals("/") )
1141 path = "";
1142 try {
1143 Context context = (Context) host.findChild(path);
1144 if (context == null) {
1145 writer.println(sm.getString("managerServlet.noContext",
1146 RequestUtil.filter(displayPath)));
1147 return;
1148 }
1149 Manager manager = context.getManager() ;
1150 if(manager == null) {
1151 writer.println(sm.getString("managerServlet.noManager",
1152 RequestUtil.filter(displayPath)));
1153 return;
1154 }
1155 int maxCount = 60;
1156 int maxInactiveInterval = manager.getMaxInactiveInterval()/60;
1157 int histoInterval = maxInactiveInterval / maxCount;
1158 if ( histoInterval * maxCount < maxInactiveInterval )
1159 histoInterval++;
1160 if (0==histoInterval)
1161 histoInterval=1;
1162 maxCount = maxInactiveInterval / histoInterval;
1163 if ( histoInterval * maxCount < maxInactiveInterval )
1164 maxCount++;
1165
1166 writer.println(sm.getString("managerServlet.sessions", displayPath));
1167 writer.println(sm.getString("managerServlet.sessiondefaultmax",
1168 "" + maxInactiveInterval));
1169 Session [] sessions = manager.findSessions();
1170 int [] timeout = new int[maxCount];
1171 int notimeout = 0;
1172 int expired = 0;
1173 long now = System.currentTimeMillis();
1174 for (int i = 0; i < sessions.length; i++) {
1175 int time = (int)((now-sessions[i].getLastAccessedTimeInternal())/1000);
1176 if (idle >= 0 && time >= idle*60) {
1177 sessions[i].expire();
1178 idle++;
1179 }
1180 time=time/60/histoInterval;
1181 if (time < 0)
1182 notimeout++;
1183 else if (time >= maxCount)
1184 timeout[maxCount-1]++;
1185 else
1186 timeout[time]++;
1187 }
1188 if (timeout[0] > 0)
1189 writer.println(sm.getString("managerServlet.sessiontimeout",
1190 "<" + histoInterval, "" + timeout[0]));
1191 for (int i = 1; i < maxCount-1; i++) {
1192 if (timeout[i] > 0)
1193 writer.println(sm.getString("managerServlet.sessiontimeout",
1194 "" + (i)*histoInterval + " - <" + (i+1)*histoInterval,
1195 "" + timeout[i]));
1196 }
1197 if (timeout[maxCount-1] > 0)
1198 writer.println(sm.getString("managerServlet.sessiontimeout",
1199 ">=" + maxCount*histoInterval,
1200 "" + timeout[maxCount-1]));
1201 if (notimeout > 0)
1202 writer.println(sm.getString("managerServlet.sessiontimeout",
1203 "unlimited","" + notimeout));
1204 if (idle >= 0)
1205 writer.println(sm.getString("managerServlet.sessiontimeout",
1206 "" + idle,"expired " + expired));
1207 } catch (Throwable t) {
1208 log("ManagerServlet.sessions[" + displayPath + "]", t);
1209 writer.println(sm.getString("managerServlet.exception",
1210 t.toString()));
1211 }
1212
1213 }
1214
1215
1216 /**
1217 * Session information for the web application at the specified context path.
1218 * Displays a profile of session lastAccessedTime listing number
1219 * of sessions for each 10 minute interval up to 10 hours.
1220 *
1221 * @param writer Writer to render to
1222 * @param path Context path of the application to list session information for
1223 */
1224 protected void sessions(PrintWriter writer, String path) {
1225 sessions(writer, path, -1);
1226 }
1227
1228
1229 /**
1230 *
1231 * Extract the expiration request parameter
1232 *
1233 * @param path
1234 * @param req
1235 */
1236 protected void expireSessions(PrintWriter writer, String path, HttpServletRequest req) {
1237 int idle = -1;
1238 String idleParam = req.getParameter("idle");
1239 if (idleParam != null) {
1240 try {
1241 idle = Integer.parseInt(idleParam);
1242 } catch (NumberFormatException e) {
1243 log("Could not parse idle parameter to an int: " + idleParam);
1244 }
1245 }
1246 sessions(writer, path, idle);
1247 }
1248
1249 /**
1250 * Start the web application at the specified context path.
1251 *
1252 * @param writer Writer to render to
1253 * @param path Context path of the application to be started
1254 */
1255 protected void start(PrintWriter writer, String path) {
1256
1257 if (debug >= 1)
1258 log("start: Starting web application at '" + path + "'");
1259
1260 if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1261 writer.println(sm.getString("managerServlet.invalidPath",
1262 RequestUtil.filter(path)));
1263 return;
1264 }
1265 String displayPath = path;
1266 if( path.equals("/") )
1267 path = "";
1268
1269 try {
1270 Context context = (Context) host.findChild(path);
1271 if (context == null) {
1272 writer.println(sm.getString("managerServlet.noContext",
1273 RequestUtil.filter(displayPath)));
1274 return;
1275 }
1276 ((Lifecycle) context).start();
1277 if (context.getAvailable())
1278 writer.println
1279 (sm.getString("managerServlet.started", displayPath));
1280 else
1281 writer.println
1282 (sm.getString("managerServlet.startFailed", displayPath));
1283 } catch (Throwable t) {
1284 getServletContext().log
1285 (sm.getString("managerServlet.startFailed", displayPath), t);
1286 writer.println
1287 (sm.getString("managerServlet.startFailed", displayPath));
1288 writer.println(sm.getString("managerServlet.exception",
1289 t.toString()));
1290 }
1291
1292 }
1293
1294
1295 /**
1296 * Stop the web application at the specified context path.
1297 *
1298 * @param writer Writer to render to
1299 * @param path Context path of the application to be stopped
1300 */
1301 protected void stop(PrintWriter writer, String path) {
1302
1303 if (debug >= 1)
1304 log("stop: Stopping web application at '" + path + "'");
1305
1306 if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1307 writer.println(sm.getString("managerServlet.invalidPath",
1308 RequestUtil.filter(path)));
1309 return;
1310 }
1311 String displayPath = path;
1312 if( path.equals("/") )
1313 path = "";
1314
1315 try {
1316 Context context = (Context) host.findChild(path);
1317 if (context == null) {
1318 writer.println(sm.getString("managerServlet.noContext",
1319 RequestUtil.filter(displayPath)));
1320 return;
1321 }
1322 // It isn't possible for the manager to stop itself
1323 if (context.getPath().equals(this.context.getPath())) {
1324 writer.println(sm.getString("managerServlet.noSelf"));
1325 return;
1326 }
1327 ((Lifecycle) context).stop();
1328 writer.println(sm.getString("managerServlet.stopped", displayPath));
1329 } catch (Throwable t) {
1330 log("ManagerServlet.stop[" + displayPath + "]", t);
1331 writer.println(sm.getString("managerServlet.exception",
1332 t.toString()));
1333 }
1334
1335 }
1336
1337
1338 /**
1339 * Undeploy the web application at the specified context path.
1340 *
1341 * @param writer Writer to render to
1342 * @param path Context path of the application to be removed
1343 */
1344 protected void undeploy(PrintWriter writer, String path) {
1345
1346 if (debug >= 1)
1347 log("undeploy: Undeploying web application at '" + path + "'");
1348
1349 if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1350 writer.println(sm.getString("managerServlet.invalidPath",
1351 RequestUtil.filter(path)));
1352 return;
1353 }
1354 String displayPath = path;
1355 if( path.equals("/") )
1356 path = "";
1357
1358 try {
1359
1360 // Validate the Context of the specified application
1361 Context context = (Context) host.findChild(path);
1362 if (context == null) {
1363 writer.println(sm.getString("managerServlet.noContext",
1364 RequestUtil.filter(displayPath)));
1365 return;
1366 }
1367
1368 // Identify the appBase of the owning Host of this Context (if any)
1369 String appBase = null;
1370 File appBaseDir = null;
1371 if (context.getParent() instanceof Host) {
1372 appBase = ((Host) context.getParent()).getAppBase();
1373 appBaseDir = new File(appBase);
1374 if (!appBaseDir.isAbsolute()) {
1375 appBaseDir = new File(System.getProperty("catalina.base"),
1376 appBase);
1377 }
1378 }
1379
1380 if (!isDeployed(path)) {
1381 writer.println(sm.getString("managerServlet.notDeployed",
1382 RequestUtil.filter(displayPath)));
1383 return;
1384 }
1385
1386 if (!isServiced(path)) {
1387 addServiced(path);
1388 try {
1389 // Try to stop the context first to be nicer
1390 ((Lifecycle) context).stop();
1391 } catch (Throwable t) {
1392 // Ignore
1393 }
1394 try {
1395 if (path.lastIndexOf('/') > 0) {
1396 path = "/" + path.substring(1).replace('/','#');
1397 }
1398 File war = new File(getAppBase(), getDocBase(path) + ".war");
1399 File dir = new File(getAppBase(), getDocBase(path));
1400 File xml = new File(configBase, getConfigFile(path) + ".xml");
1401 if (war.exists()) {
1402 war.delete();
1403 } else if (dir.exists()) {
1404 undeployDir(dir);
1405 } else {
1406 xml.delete();
1407 }
1408 // Perform new deployment
1409 check(path.replace('#', '/'));
1410 } finally {
1411 removeServiced(path.replace('#','/'));
1412 }
1413 }
1414 writer.println(sm.getString("managerServlet.undeployed",
1415 displayPath));
1416 } catch (Throwable t) {
1417 log("ManagerServlet.undeploy[" + displayPath + "]", t);
1418 writer.println(sm.getString("managerServlet.exception",
1419 t.toString()));
1420 }
1421
1422 }
1423
1424
1425 // -------------------------------------------------------- Support Methods
1426
1427
1428 /**
1429 * Given a context path, get the config file name.
1430 */
1431 protected String getConfigFile(String path) {
1432 String basename = null;
1433 if (path.equals("")) {
1434 basename = "ROOT";
1435 } else {
1436 basename = path.substring(1).replace('/', '#');
1437 }
1438 return (basename);
1439 }
1440
1441
1442 /**
1443 * Given a context path, get the doc base.
1444 */
1445 protected String getDocBase(String path) {
1446 String basename = null;
1447 if (path.equals("")) {
1448 basename = "ROOT";
1449 } else {
1450 basename = path.substring(1).replace('/', '#');
1451 }
1452 return (basename);
1453 }
1454
1455
1456 /**
1457 * Return a File object representing the "application root" directory
1458 * for our associated Host.
1459 */
1460 protected File getAppBase() {
1461
1462 if (appBase != null) {
1463 return appBase;
1464 }
1465
1466 File file = new File(host.getAppBase());
1467 if (!file.isAbsolute())
1468 file = new File(System.getProperty("catalina.base"),
1469 host.getAppBase());
1470 try {
1471 appBase = file.getCanonicalFile();
1472 } catch (IOException e) {
1473 appBase = file;
1474 }
1475 return (appBase);
1476
1477 }
1478
1479
1480 /**
1481 * Invoke the isDeployed method on the deployer.
1482 */
1483 protected boolean isDeployed(String name)
1484 throws Exception {
1485 String[] params = { name };
1486 String[] signature = { "java.lang.String" };
1487 Boolean result =
1488 (Boolean) mBeanServer.invoke(oname, "isDeployed", params, signature);
1489 return result.booleanValue();
1490 }
1491
1492
1493 /**
1494 * Invoke the check method on the deployer.
1495 */
1496 protected void check(String name)
1497 throws Exception {
1498 String[] params = { name };
1499 String[] signature = { "java.lang.String" };
1500 mBeanServer.invoke(oname, "check", params, signature);
1501 }
1502
1503
1504 /**
1505 * Invoke the isServiced method on the deployer.
1506 */
1507 protected boolean isServiced(String name)
1508 throws Exception {
1509 String[] params = { name };
1510 String[] signature = { "java.lang.String" };
1511 Boolean result =
1512 (Boolean) mBeanServer.invoke(oname, "isServiced", params, signature);
1513 return result.booleanValue();
1514 }
1515
1516
1517 /**
1518 * Invoke the addServiced method on the deployer.
1519 */
1520 protected void addServiced(String name)
1521 throws Exception {
1522 String[] params = { name };
1523 String[] signature = { "java.lang.String" };
1524 mBeanServer.invoke(oname, "addServiced", params, signature);
1525 }
1526
1527
1528 /**
1529 * Invoke the removeServiced method on the deployer.
1530 */
1531 protected void removeServiced(String name)
1532 throws Exception {
1533 String[] params = { name };
1534 String[] signature = { "java.lang.String" };
1535 mBeanServer.invoke(oname, "removeServiced", params, signature);
1536 }
1537
1538
1539 /**
1540 * Delete the specified directory, including all of its contents and
1541 * subdirectories recursively.
1542 *
1543 * @param dir File object representing the directory to be deleted
1544 */
1545 protected void undeployDir(File dir) {
1546
1547 String files[] = dir.list();
1548 if (files == null) {
1549 files = new String[0];
1550 }
1551 for (int i = 0; i < files.length; i++) {
1552 File file = new File(dir, files[i]);
1553 if (file.isDirectory()) {
1554 undeployDir(file);
1555 } else {
1556 file.delete();
1557 }
1558 }
1559 dir.delete();
1560
1561 }
1562
1563
1564 /**
1565 * Upload the WAR file included in this request, and store it at the
1566 * specified file location.
1567 *
1568 * @param request The servlet request we are processing
1569 * @param war The file into which we should store the uploaded WAR
1570 *
1571 * @exception IOException if an I/O error occurs during processing
1572 */
1573 protected void uploadWar(HttpServletRequest request, File war)
1574 throws IOException {
1575
1576 war.delete();
1577 ServletInputStream istream = null;
1578 BufferedOutputStream ostream = null;
1579 try {
1580 istream = request.getInputStream();
1581 ostream =
1582 new BufferedOutputStream(new FileOutputStream(war), 1024);
1583 byte buffer[] = new byte[1024];
1584 while (true) {
1585 int n = istream.read(buffer);
1586 if (n < 0) {
1587 break;
1588 }
1589 ostream.write(buffer, 0, n);
1590 }
1591 ostream.flush();
1592 ostream.close();
1593 ostream = null;
1594 istream.close();
1595 istream = null;
1596 } catch (IOException e) {
1597 war.delete();
1598 throw e;
1599 } finally {
1600 if (ostream != null) {
1601 try {
1602 ostream.close();
1603 } catch (Throwable t) {
1604 ;
1605 }
1606 ostream = null;
1607 }
1608 if (istream != null) {
1609 try {
1610 istream.close();
1611 } catch (Throwable t) {
1612 ;
1613 }
1614 istream = null;
1615 }
1616 }
1617
1618 }
1619
1620
1621 /**
1622 * Copy the specified file or directory to the destination.
1623 *
1624 * @param src File object representing the source
1625 * @param dest File object representing the destination
1626 */
1627 public static boolean copy(File src, File dest) {
1628 boolean result = false;
1629 try {
1630 if( src != null &&
1631 !src.getCanonicalPath().equals(dest.getCanonicalPath()) ) {
1632 result = copyInternal(src, dest, new byte[4096]);
1633 }
1634 } catch (IOException e) {
1635 e.printStackTrace();
1636 }
1637 return result;
1638 }
1639
1640
1641 /**
1642 * Copy the specified file or directory to the destination.
1643 *
1644 * @param src File object representing the source
1645 * @param dest File object representing the destination
1646 */
1647 public static boolean copyInternal(File src, File dest, byte[] buf) {
1648
1649 boolean result = true;
1650
1651 String files[] = null;
1652 if (src.isDirectory()) {
1653 files = src.list();
1654 result = dest.mkdir();
1655 } else {
1656 files = new String[1];
1657 files[0] = "";
1658 }
1659 if (files == null) {
1660 files = new String[0];
1661 }
1662 for (int i = 0; (i < files.length) && result; i++) {
1663 File fileSrc = new File(src, files[i]);
1664 File fileDest = new File(dest, files[i]);
1665 if (fileSrc.isDirectory()) {
1666 result = copyInternal(fileSrc, fileDest, buf);
1667 } else {
1668 FileInputStream is = null;
1669 FileOutputStream os = null;
1670 try {
1671 is = new FileInputStream(fileSrc);
1672 os = new FileOutputStream(fileDest);
1673 int len = 0;
1674 while (true) {
1675 len = is.read(buf);
1676 if (len == -1)
1677 break;
1678 os.write(buf, 0, len);
1679 }
1680 } catch (IOException e) {
1681 e.printStackTrace();
1682 result = false;
1683 } finally {
1684 if (is != null) {
1685 try {
1686 is.close();
1687 } catch (IOException e) {
1688 }
1689 }
1690 if (os != null) {
1691 try {
1692 os.close();
1693 } catch (IOException e) {
1694 }
1695 }
1696 }
1697 }
1698 }
1699 return result;
1700
1701 }
1702
1703
1704 }