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