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