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 import java.io.File;
22 import java.io.IOException;
23 import java.io.PrintWriter;
24 import java.io.StringWriter;
25 import java.text.MessageFormat;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.Date;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.TreeMap;
34 import javax.servlet.ServletException;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37 import javax.servlet.http.HttpSession;
38
39 import org.apache.catalina.Container;
40 import org.apache.catalina.Context;
41 import org.apache.catalina.Manager;
42 import org.apache.catalina.Session;
43 import org.apache.catalina.manager.util.BaseSessionComparator;
44 import org.apache.catalina.manager.util.ReverseComparator;
45 import org.apache.catalina.manager.util.SessionUtils;
46 import org.apache.catalina.util.RequestUtil;
47 import org.apache.catalina.util.ServerInfo;
48 import org.apache.catalina.util.URLEncoder;
49 import org.apache.tomcat.util.http.fileupload.DiskFileUpload;
50 import org.apache.tomcat.util.http.fileupload.FileItem;
51
52 /**
53 * Servlet that enables remote management of the web applications deployed
54 * within the same virtual host as this web application is. Normally, this
55 * functionality will be protected by a security constraint in the web
56 * application deployment descriptor. However, this requirement can be
57 * relaxed during testing.
58 * <p>
59 * The difference between the <code>ManagerServlet</code> and this
60 * Servlet is that this Servlet prints out a HTML interface which
61 * makes it easier to administrate.
62 * <p>
63 * However if you use a software that parses the output of
64 * <code>ManagerServlet</code> you won't be able to upgrade
65 * to this Servlet since the output are not in the
66 * same format ar from <code>ManagerServlet</code>
67 *
68 * @author Bip Thelin
69 * @author Malcolm Edgar
70 * @author Glenn L. Nielsen
71 * @version $Revision: 918620 $, $Date: 2010-03-03 19:44:19 +0100 (Wed, 03 Mar 2010) $
72 * @see ManagerServlet
73 */
74
75 public final class HTMLManagerServlet extends ManagerServlet {
76
77 protected static final URLEncoder URL_ENCODER;
78 protected static final String APPLICATION_MESSAGE = "message";
79 protected static final String APPLICATION_ERROR = "error";
80 protected String sessionsListJspPath = "/WEB-INF/jsp/sessionsList.jsp";
81 protected String sessionDetailJspPath = "/WEB-INF/jsp/sessionDetail.jsp";
82
83 static {
84 URL_ENCODER = new URLEncoder();
85 // '/' should not be encoded in context paths
86 URL_ENCODER.addSafeCharacter('/');
87 }
88 // --------------------------------------------------------- Public Methods
89
90 /**
91 * Process a GET request for the specified resource.
92 *
93 * @param request The servlet request we are processing
94 * @param response The servlet response we are creating
95 *
96 * @exception IOException if an input/output error occurs
97 * @exception ServletException if a servlet-specified error occurs
98 */
99 public void doGet(HttpServletRequest request,
100 HttpServletResponse response)
101 throws IOException, ServletException {
102
103 // Identify the request parameters that we need
104 String command = request.getPathInfo();
105
106 String path = request.getParameter("path");
107 String deployPath = request.getParameter("deployPath");
108 String deployConfig = request.getParameter("deployConfig");
109 String deployWar = request.getParameter("deployWar");
110
111 // Prepare our output writer to generate the response message
112 response.setContentType("text/html; charset=" + Constants.CHARSET);
113
114 String message = "";
115 // Process the requested command
116 if (command == null || command.equals("/")) {
117 } else if (command.equals("/deploy")) {
118 message = deployInternal(deployConfig, deployPath, deployWar);
119 } else if (command.equals("/list")) {
120 } else if (command.equals("/reload")) {
121 message = reload(path);
122 } else if (command.equals("/undeploy")) {
123 message = undeploy(path);
124 } else if (command.equals("/expire")) {
125 message = expireSessions(path, request);
126 } else if (command.equals("/sessions")) {
127 try {
128 doSessions(path, request, response);
129 return;
130 } catch (Exception e) {
131 log("HTMLManagerServlet.sessions[" + path + "]", e);
132 message = sm.getString("managerServlet.exception",
133 e.toString());
134 }
135 } else if (command.equals("/start")) {
136 message = start(path);
137 } else if (command.equals("/stop")) {
138 message = stop(path);
139 } else if (command.equals("/findleaks")) {
140 message = findleaks();
141 } else {
142 message =
143 sm.getString("managerServlet.unknownCommand", command);
144 }
145
146 list(request, response, message);
147 }
148
149 /**
150 * Process a POST request for the specified resource.
151 *
152 * @param request The servlet request we are processing
153 * @param response The servlet response we are creating
154 *
155 * @exception IOException if an input/output error occurs
156 * @exception ServletException if a servlet-specified error occurs
157 */
158 public void doPost(HttpServletRequest request,
159 HttpServletResponse response)
160 throws IOException, ServletException {
161
162 // Identify the request parameters that we need
163 String command = request.getPathInfo();
164
165 if (command == null || !command.equals("/upload")) {
166 doGet(request,response);
167 return;
168 }
169
170 // Prepare our output writer to generate the response message
171 response.setContentType("text/html; charset=" + Constants.CHARSET);
172
173 String message = "";
174
175 // Create a new file upload handler
176 DiskFileUpload upload = new DiskFileUpload();
177
178 // Get the tempdir
179 File tempdir = (File) getServletContext().getAttribute
180 ("javax.servlet.context.tempdir");
181 // Set upload parameters
182 upload.setSizeMax(-1);
183 upload.setRepositoryPath(tempdir.getCanonicalPath());
184
185 // Parse the request
186 String basename = null;
187 String war = null;
188 FileItem warUpload = null;
189 try {
190 List items = upload.parseRequest(request);
191
192 // Process the uploaded fields
193 Iterator iter = items.iterator();
194 while (iter.hasNext()) {
195 FileItem item = (FileItem) iter.next();
196
197 if (!item.isFormField()) {
198 if (item.getFieldName().equals("deployWar") &&
199 warUpload == null) {
200 warUpload = item;
201 } else {
202 item.delete();
203 }
204 }
205 }
206 while (true) {
207 if (warUpload == null) {
208 message = sm.getString
209 ("htmlManagerServlet.deployUploadNoFile");
210 break;
211 }
212 war = warUpload.getName();
213 if (!war.toLowerCase().endsWith(".war")) {
214 message = sm.getString
215 ("htmlManagerServlet.deployUploadNotWar",war);
216 break;
217 }
218 // Get the filename if uploaded name includes a path
219 if (war.lastIndexOf('\\') >= 0) {
220 war = war.substring(war.lastIndexOf('\\') + 1);
221 }
222 if (war.lastIndexOf('/') >= 0) {
223 war = war.substring(war.lastIndexOf('/') + 1);
224 }
225 // Identify the appBase of the owning Host of this Context
226 // (if any)
227 basename = war.substring(0, war.toLowerCase().indexOf(".war"));
228 File file = new File(getAppBase(), war);
229 if (file.exists()) {
230 message = sm.getString
231 ("htmlManagerServlet.deployUploadWarExists",war);
232 break;
233 }
234 String path = null;
235 if (basename.equals("ROOT")) {
236 path = "";
237 } else {
238 path = "/" + basename.replace('#', '/');
239 }
240
241 if ((host.findChild(path) != null) && !isDeployed(path)) {
242 message = sm.getString
243 ("htmlManagerServlet.deployUploadInServerXml", war);
244 break;
245 }
246
247 if (!isServiced(path)) {
248 addServiced(path);
249 try {
250 warUpload.write(file);
251 // Perform new deployment
252 check(path);
253 } finally {
254 removeServiced(path);
255 }
256 }
257 break;
258 }
259 } catch(Exception e) {
260 message = sm.getString
261 ("htmlManagerServlet.deployUploadFail", e.getMessage());
262 log(message, e);
263 } finally {
264 if (warUpload != null) {
265 warUpload.delete();
266 }
267 warUpload = null;
268 }
269
270 list(request, response, message);
271 }
272
273 /**
274 * Deploy an application for the specified path from the specified
275 * web application archive.
276 *
277 * @param config URL of the context configuration file to be deployed
278 * @param path Context path of the application to be deployed
279 * @param war URL of the web application archive to be deployed
280 * @return message String
281 */
282 protected String deployInternal(String config, String path, String war) {
283
284 StringWriter stringWriter = new StringWriter();
285 PrintWriter printWriter = new PrintWriter(stringWriter);
286
287 super.deploy(printWriter, config, path, war, false);
288
289 return stringWriter.toString();
290 }
291
292 /**
293 * Render a HTML list of the currently active Contexts in our virtual host,
294 * and memory and server status information.
295 *
296 * @param request The request
297 * @param response The response
298 * @param message a message to display
299 */
300 public void list(HttpServletRequest request,
301 HttpServletResponse response,
302 String message) throws IOException {
303
304 if (debug >= 1)
305 log("list: Listing contexts for virtual host '" +
306 host.getName() + "'");
307
308 PrintWriter writer = response.getWriter();
309
310 // HTML Header Section
311 writer.print(Constants.HTML_HEADER_SECTION);
312
313 // Body Header Section
314 Object[] args = new Object[2];
315 args[0] = request.getContextPath();
316 args[1] = sm.getString("htmlManagerServlet.title");
317 writer.print(MessageFormat.format
318 (Constants.BODY_HEADER_SECTION, args));
319
320 // Message Section
321 args = new Object[3];
322 args[0] = sm.getString("htmlManagerServlet.messageLabel");
323 if (message == null || message.length() == 0) {
324 args[1] = "OK";
325 } else {
326 args[1] = RequestUtil.filter(message);
327 }
328 writer.print(MessageFormat.format(Constants.MESSAGE_SECTION, args));
329
330 // Manager Section
331 args = new Object[9];
332 args[0] = sm.getString("htmlManagerServlet.manager");
333 args[1] = response.encodeURL(request.getContextPath() + "/html/list");
334 args[2] = sm.getString("htmlManagerServlet.list");
335 args[3] = response.encodeURL
336 (request.getContextPath() + "/" +
337 sm.getString("htmlManagerServlet.helpHtmlManagerFile"));
338 args[4] = sm.getString("htmlManagerServlet.helpHtmlManager");
339 args[5] = response.encodeURL
340 (request.getContextPath() + "/" +
341 sm.getString("htmlManagerServlet.helpManagerFile"));
342 args[6] = sm.getString("htmlManagerServlet.helpManager");
343 args[7] = response.encodeURL
344 (request.getContextPath() + "/status");
345 args[8] = sm.getString("statusServlet.title");
346 writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args));
347
348 // Apps Header Section
349 args = new Object[6];
350 args[0] = sm.getString("htmlManagerServlet.appsTitle");
351 args[1] = sm.getString("htmlManagerServlet.appsPath");
352 args[2] = sm.getString("htmlManagerServlet.appsName");
353 args[3] = sm.getString("htmlManagerServlet.appsAvailable");
354 args[4] = sm.getString("htmlManagerServlet.appsSessions");
355 args[5] = sm.getString("htmlManagerServlet.appsTasks");
356 writer.print(MessageFormat.format(APPS_HEADER_SECTION, args));
357
358 // Apps Row Section
359 // Create sorted map of deployed applications context paths.
360 Container children[] = host.findChildren();
361 String contextPaths[] = new String[children.length];
362 for (int i = 0; i < children.length; i++)
363 contextPaths[i] = children[i].getName();
364
365 TreeMap sortedContextPathsMap = new TreeMap();
366
367 for (int i = 0; i < contextPaths.length; i++) {
368 String displayPath = contextPaths[i];
369 sortedContextPathsMap.put(displayPath, contextPaths[i]);
370 }
371
372 String appsStart = sm.getString("htmlManagerServlet.appsStart");
373 String appsStop = sm.getString("htmlManagerServlet.appsStop");
374 String appsReload = sm.getString("htmlManagerServlet.appsReload");
375 String appsUndeploy = sm.getString("htmlManagerServlet.appsUndeploy");
376 String appsExpire = sm.getString("htmlManagerServlet.appsExpire");
377
378 Iterator iterator = sortedContextPathsMap.entrySet().iterator();
379 boolean isHighlighted = true;
380 boolean isDeployed = true;
381 String highlightColor = null;
382
383 while (iterator.hasNext()) {
384 // Bugzilla 34818, alternating row colors
385 isHighlighted = !isHighlighted;
386 if(isHighlighted) {
387 highlightColor = "#C3F3C3";
388 } else {
389 highlightColor = "#FFFFFF";
390 }
391
392 Map.Entry entry = (Map.Entry) iterator.next();
393 String displayPath = (String) entry.getKey();
394 String contextPath = (String) entry.getValue();
395 Context context = (Context) host.findChild(contextPath);
396 if (displayPath.equals("")) {
397 displayPath = "/";
398 }
399
400 if (context != null ) {
401 try {
402 isDeployed = isDeployed(contextPath);
403 } catch (Exception e) {
404 // Assume false on failure for safety
405 isDeployed = false;
406 }
407
408 args = new Object[7];
409 args[0] = URL_ENCODER.encode(displayPath);
410 args[1] = displayPath;
411 args[2] = context.getDisplayName();
412 if (args[2] == null) {
413 args[2] = " ";
414 }
415 args[3] = new Boolean(context.getAvailable());
416 args[4] = response.encodeURL
417 (request.getContextPath() +
418 "/html/sessions?path=" + URL_ENCODER.encode(displayPath));
419 if (context.getManager() != null) {
420 args[5] = new Integer
421 (context.getManager().getActiveSessions());
422 } else {
423 args[5] = new Integer(0);
424 }
425
426 args[6] = highlightColor;
427
428 writer.print
429 (MessageFormat.format(APPS_ROW_DETAILS_SECTION, args));
430
431 args = new Object[14];
432 args[0] = response.encodeURL
433 (request.getContextPath() +
434 "/html/start?path=" + URL_ENCODER.encode(displayPath));
435 args[1] = appsStart;
436 args[2] = response.encodeURL
437 (request.getContextPath() +
438 "/html/stop?path=" + URL_ENCODER.encode(displayPath));
439 args[3] = appsStop;
440 args[4] = response.encodeURL
441 (request.getContextPath() +
442 "/html/reload?path=" + URL_ENCODER.encode(displayPath));
443 args[5] = appsReload;
444 args[6] = response.encodeURL
445 (request.getContextPath() +
446 "/html/undeploy?path=" + URL_ENCODER.encode(displayPath));
447 args[7] = appsUndeploy;
448
449 args[8] = response.encodeURL
450 (request.getContextPath() +
451 "/html/expire?path=" + URL_ENCODER.encode(displayPath));
452 args[9] = appsExpire;
453 args[10] = sm.getString("htmlManagerServlet.expire.explain");
454 Manager manager = context.getManager();
455 if (manager == null) {
456 args[11] = sm.getString("htmlManagerServlet.noManager");
457 } else {
458 args[11] = new Integer(
459 context.getManager().getMaxInactiveInterval()/60);
460 }
461 args[12] = sm.getString("htmlManagerServlet.expire.unit");
462
463 args[13] = highlightColor;
464
465 if (context.getPath().equals(this.context.getPath())) {
466 writer.print(MessageFormat.format(
467 MANAGER_APP_ROW_BUTTON_SECTION, args));
468 } else if (context.getAvailable() && isDeployed) {
469 writer.print(MessageFormat.format(
470 STARTED_DEPLOYED_APPS_ROW_BUTTON_SECTION, args));
471 } else if (context.getAvailable() && !isDeployed) {
472 writer.print(MessageFormat.format(
473 STARTED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION, args));
474 } else if (!context.getAvailable() && isDeployed) {
475 writer.print(MessageFormat.format(
476 STOPPED_DEPLOYED_APPS_ROW_BUTTON_SECTION, args));
477 } else {
478 writer.print(MessageFormat.format(
479 STOPPED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION, args));
480 }
481
482 }
483 }
484
485 // Deploy Section
486 args = new Object[7];
487 args[0] = sm.getString("htmlManagerServlet.deployTitle");
488 args[1] = sm.getString("htmlManagerServlet.deployServer");
489 args[2] = response.encodeURL(request.getContextPath() + "/html/deploy");
490 args[3] = sm.getString("htmlManagerServlet.deployPath");
491 args[4] = sm.getString("htmlManagerServlet.deployConfig");
492 args[5] = sm.getString("htmlManagerServlet.deployWar");
493 args[6] = sm.getString("htmlManagerServlet.deployButton");
494 writer.print(MessageFormat.format(DEPLOY_SECTION, args));
495
496 args = new Object[4];
497 args[0] = sm.getString("htmlManagerServlet.deployUpload");
498 args[1] = response.encodeURL(request.getContextPath() + "/html/upload");
499 args[2] = sm.getString("htmlManagerServlet.deployUploadFile");
500 args[3] = sm.getString("htmlManagerServlet.deployButton");
501 writer.print(MessageFormat.format(UPLOAD_SECTION, args));
502
503 // Diagnostics section
504 args = new Object[5];
505 args[0] = sm.getString("htmlManagerServlet.diagnosticsTitle");
506 args[1] = sm.getString("htmlManagerServlet.diagnosticsLeak");
507 args[2] = response.encodeURL(request.getContextPath() +
508 "/html/findleaks");
509 args[3] = sm.getString("htmlManagerServlet.diagnosticsLeakWarning");
510 args[4] = sm.getString("htmlManagerServlet.diagnosticsLeakButton");
511 writer.print(MessageFormat.format(DIAGNOSTICS_SECTION, args));
512
513 // Server Header Section
514 args = new Object[7];
515 args[0] = sm.getString("htmlManagerServlet.serverTitle");
516 args[1] = sm.getString("htmlManagerServlet.serverVersion");
517 args[2] = sm.getString("htmlManagerServlet.serverJVMVersion");
518 args[3] = sm.getString("htmlManagerServlet.serverJVMVendor");
519 args[4] = sm.getString("htmlManagerServlet.serverOSName");
520 args[5] = sm.getString("htmlManagerServlet.serverOSVersion");
521 args[6] = sm.getString("htmlManagerServlet.serverOSArch");
522 writer.print(MessageFormat.format
523 (Constants.SERVER_HEADER_SECTION, args));
524
525 // Server Row Section
526 args = new Object[6];
527 args[0] = ServerInfo.getServerInfo();
528 args[1] = System.getProperty("java.runtime.version");
529 args[2] = System.getProperty("java.vm.vendor");
530 args[3] = System.getProperty("os.name");
531 args[4] = System.getProperty("os.version");
532 args[5] = System.getProperty("os.arch");
533 writer.print(MessageFormat.format(Constants.SERVER_ROW_SECTION, args));
534
535 // HTML Tail Section
536 writer.print(Constants.HTML_TAIL_SECTION);
537
538 // Finish up the response
539 writer.flush();
540 writer.close();
541 }
542
543 /**
544 * Reload the web application at the specified context path.
545 *
546 * @see ManagerServlet#reload(PrintWriter, String)
547 *
548 * @param path Context path of the application to be restarted
549 * @return message String
550 */
551 protected String reload(String path) {
552
553 StringWriter stringWriter = new StringWriter();
554 PrintWriter printWriter = new PrintWriter(stringWriter);
555
556 super.reload(printWriter, path);
557
558 return stringWriter.toString();
559 }
560
561 /**
562 * Undeploy the web application at the specified context path.
563 *
564 * @see ManagerServlet#undeploy(PrintWriter, String)
565 *
566 * @param path Context path of the application to be undeployd
567 * @return message String
568 */
569 protected String undeploy(String path) {
570
571 StringWriter stringWriter = new StringWriter();
572 PrintWriter printWriter = new PrintWriter(stringWriter);
573
574 super.undeploy(printWriter, path);
575
576 return stringWriter.toString();
577 }
578
579 /**
580 * Display session information and invoke list.
581 *
582 * @see ManagerServlet#sessions(PrintWriter, String, int)
583 *
584 * @param path Context path of the application to list session information
585 * @param idle Expire all sessions with idle time ≥ idle for this context
586 * @return message String
587 */
588 public String sessions(String path, int idle) {
589
590 StringWriter stringWriter = new StringWriter();
591 PrintWriter printWriter = new PrintWriter(stringWriter);
592
593 super.sessions(printWriter, path, idle);
594
595 return stringWriter.toString();
596 }
597
598 /**
599 * Display session information and invoke list.
600 *
601 * @see ManagerServlet#sessions(PrintWriter, String)
602 *
603 * @param path Context path of the application to list session information
604 * @return message String
605 */
606 public String sessions(String path) {
607
608 return sessions(path, -1);
609 }
610
611 /**
612 * Start the web application at the specified context path.
613 *
614 * @see ManagerServlet#start(PrintWriter, String)
615 *
616 * @param path Context path of the application to be started
617 * @return message String
618 */
619 public String start(String path) {
620
621 StringWriter stringWriter = new StringWriter();
622 PrintWriter printWriter = new PrintWriter(stringWriter);
623
624 super.start(printWriter, path);
625
626 return stringWriter.toString();
627 }
628
629 /**
630 * Stop the web application at the specified context path.
631 *
632 * @see ManagerServlet#stop(PrintWriter, String)
633 *
634 * @param path Context path of the application to be stopped
635 * @return message String
636 */
637 protected String stop(String path) {
638
639 StringWriter stringWriter = new StringWriter();
640 PrintWriter printWriter = new PrintWriter(stringWriter);
641
642 super.stop(printWriter, path);
643
644 return stringWriter.toString();
645 }
646
647 /**
648 * Find potential memory leaks caused by web application reload.
649 *
650 * @see ManagerServlet#findleaks(PrintWriter)
651 *
652 * @return message String
653 */
654 protected String findleaks() {
655
656 StringBuilder msg = new StringBuilder();
657
658 StringWriter stringWriter = new StringWriter();
659 PrintWriter printWriter = new PrintWriter(stringWriter);
660
661 super.findleaks(printWriter);
662
663 if (stringWriter.getBuffer().length() > 0) {
664 msg.append(sm.getString("htmlManagerServlet.findleaksList"));
665 msg.append(stringWriter.toString());
666 } else {
667 msg.append(sm.getString("htmlManagerServlet.findleaksNone"));
668 }
669
670 return msg.toString();
671 }
672
673
674 /**
675 * @see javax.servlet.Servlet#getServletInfo()
676 */
677 public String getServletInfo() {
678 return "HTMLManagerServlet, Copyright (c) 1999-2010, The Apache Software Foundation";
679 }
680
681 /**
682 * @see javax.servlet.GenericServlet#init()
683 */
684 public void init() throws ServletException {
685 super.init();
686 }
687
688 // ------------------------------------------------ Sessions administration
689
690 /**
691 *
692 * Extract the expiration request parameter
693 *
694 * @param path
695 * @param req
696 */
697 protected String expireSessions(String path, HttpServletRequest req) {
698 int idle = -1;
699 String idleParam = req.getParameter("idle");
700 if (idleParam != null) {
701 try {
702 idle = Integer.parseInt(idleParam);
703 } catch (NumberFormatException e) {
704 log("Could not parse idle parameter to an int: " + idleParam);
705 }
706 }
707 return sessions(path, idle);
708 }
709
710 /**
711 *
712 * @param req
713 * @param resp
714 * @throws ServletException
715 * @throws IOException
716 */
717 protected void doSessions(String path, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
718 req.setAttribute("path", path);
719 String action = req.getParameter("action");
720 if (debug >= 1) {
721 log("sessions: Session action '" + action + "' for web application at '" + path + "'");
722 }
723 if ("sessionDetail".equals(action)) {
724 String sessionId = req.getParameter("sessionId");
725 displaySessionDetailPage(req, resp, path, sessionId);
726 return;
727 } else if ("invalidateSessions".equals(action)) {
728 String[] sessionIds = req.getParameterValues("sessionIds");
729 int i = invalidateSessions(path, sessionIds);
730 req.setAttribute(APPLICATION_MESSAGE, "" + i + " sessions invalidated.");
731 } else if ("removeSessionAttribute".equals(action)) {
732 String sessionId = req.getParameter("sessionId");
733 String name = req.getParameter("attributeName");
734 boolean removed = removeSessionAttribute(path, sessionId, name);
735 String outMessage = removed ? "Session attribute '" + name + "' removed." : "Session did not contain any attribute named '" + name + "'";
736 req.setAttribute(APPLICATION_MESSAGE, outMessage);
737 resp.sendRedirect(resp.encodeRedirectURL(req.getRequestURL().append("?path=").append(path).append("&action=sessionDetail&sessionId=").append(sessionId).toString()));
738 return;
739 } // else
740 displaySessionsListPage(path, req, resp);
741 }
742
743 protected Session[] getSessionsForPath(String path) {
744 if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
745 throw new IllegalArgumentException(sm.getString("managerServlet.invalidPath",
746 RequestUtil.filter(path)));
747 }
748 String displayPath = path;
749 if( path.equals("/") )
750 path = "";
751 Context context = (Context) host.findChild(path);
752 if (null == context) {
753 throw new IllegalArgumentException(sm.getString("managerServlet.noContext",
754 RequestUtil.filter(displayPath)));
755 }
756 Session[] sessions = context.getManager().findSessions();
757 return sessions;
758 }
759 protected Session getSessionForPathAndId(String path, String id) throws IOException {
760 if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
761 throw new IllegalArgumentException(sm.getString("managerServlet.invalidPath",
762 RequestUtil.filter(path)));
763 }
764 String displayPath = path;
765 if( path.equals("/") )
766 path = "";
767 Context context = (Context) host.findChild(path);
768 if (null == context) {
769 throw new IllegalArgumentException(sm.getString("managerServlet.noContext",
770 RequestUtil.filter(displayPath)));
771 }
772 Session session = context.getManager().findSession(id);
773 return session;
774 }
775
776 /**
777 *
778 * @param req
779 * @param resp
780 * @throws ServletException
781 * @throws IOException
782 */
783 protected void displaySessionsListPage(String path, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
784 List/*<Session>*/ activeSessions = Arrays.asList(getSessionsForPath(path));
785 String sortBy = req.getParameter("sort");
786 String orderBy = null;
787 if (null != sortBy && !"".equals(sortBy.trim())) {
788 Comparator comparator = getComparator(sortBy);
789 if (comparator != null) {
790 orderBy = req.getParameter("order");
791 if ("DESC".equalsIgnoreCase(orderBy)) {
792 comparator = new ReverseComparator(comparator);
793 // orderBy = "ASC";
794 } else {
795 //orderBy = "DESC";
796 }
797 try {
798 Collections.sort(activeSessions, comparator);
799 } catch (IllegalStateException ise) {
800 // at least 1 of the sessions is invalidated
801 req.setAttribute(APPLICATION_ERROR, "Can't sort session list: one session is invalidated");
802 }
803 } else {
804 log("WARNING: unknown sort order: " + sortBy);
805 }
806 }
807 // keep sort order
808 req.setAttribute("sort", sortBy);
809 req.setAttribute("order", orderBy);
810 req.setAttribute("activeSessions", activeSessions);
811 //strong>NOTE</strong> - This header will be overridden
812 // automatically if a <code>RequestDispatcher.forward()</code> call is
813 // ultimately invoked.
814 resp.setHeader("Pragma", "No-cache"); // HTTP 1.0
815 resp.setHeader("Cache-Control", "no-cache,no-store,max-age=0"); // HTTP 1.1
816 resp.setDateHeader("Expires", 0); // 0 means now
817 getServletContext().getRequestDispatcher(sessionsListJspPath).include(req, resp);
818 }
819
820 /**
821 *
822 * @param req
823 * @param resp
824 * @throws ServletException
825 * @throws IOException
826 */
827 protected void displaySessionDetailPage(HttpServletRequest req, HttpServletResponse resp, String path, String sessionId) throws ServletException, IOException {
828 Session session = getSessionForPathAndId(path, sessionId);
829 //strong>NOTE</strong> - This header will be overridden
830 // automatically if a <code>RequestDispatcher.forward()</code> call is
831 // ultimately invoked.
832 resp.setHeader("Pragma", "No-cache"); // HTTP 1.0
833 resp.setHeader("Cache-Control", "no-cache,no-store,max-age=0"); // HTTP 1.1
834 resp.setDateHeader("Expires", 0); // 0 means now
835 req.setAttribute("currentSession", session);
836 getServletContext().getRequestDispatcher(sessionDetailJspPath).include(req, resp);
837 }
838
839 /**
840 * Invalidate HttpSessions
841 * @param sessionIds
842 * @return number of invalidated sessions
843 * @throws IOException
844 */
845 public int invalidateSessions(String path, String[] sessionIds) throws IOException {
846 if (null == sessionIds) {
847 return 0;
848 }
849 int nbAffectedSessions = 0;
850 for (int i = 0; i < sessionIds.length; ++i) {
851 String sessionId = sessionIds[i];
852 HttpSession session = getSessionForPathAndId(path, sessionId).getSession();
853 if (null == session) {
854 // Shouldn't happen, but let's play nice...
855 if (debug >= 1) {
856 log("WARNING: can't invalidate null session " + sessionId);
857 }
858 continue;
859 }
860 try {
861 session.invalidate();
862 ++nbAffectedSessions;
863 if (debug >= 1) {
864 log("Invalidating session id " + sessionId);
865 }
866 } catch (IllegalStateException ise) {
867 if (debug >= 1) {
868 log("Can't invalidate already invalidated session id " + sessionId);
869 }
870 }
871 }
872 return nbAffectedSessions;
873 }
874
875 /**
876 * Removes an attribute from an HttpSession
877 * @param sessionId
878 * @param attributeName
879 * @return true if there was an attribute removed, false otherwise
880 * @throws IOException
881 */
882 public boolean removeSessionAttribute(String path, String sessionId, String attributeName) throws IOException {
883 HttpSession session = getSessionForPathAndId(path, sessionId).getSession();
884 if (null == session) {
885 // Shouldn't happen, but let's play nice...
886 if (debug >= 1) {
887 log("WARNING: can't remove attribute '" + attributeName + "' for null session " + sessionId);
888 }
889 return false;
890 }
891 boolean wasPresent = (null != session.getAttribute(attributeName));
892 try {
893 session.removeAttribute(attributeName);
894 } catch (IllegalStateException ise) {
895 if (debug >= 1) {
896 log("Can't remote attribute '" + attributeName + "' for invalidated session id " + sessionId);
897 }
898 }
899 return wasPresent;
900 }
901
902 /**
903 * Sets the maximum inactive interval (session timeout) an HttpSession
904 * @param sessionId
905 * @param maxInactiveInterval in seconds
906 * @return old value for maxInactiveInterval
907 * @throws IOException
908 */
909 public int setSessionMaxInactiveInterval(String path, String sessionId, int maxInactiveInterval) throws IOException {
910 HttpSession session = getSessionForPathAndId(path, sessionId).getSession();
911 if (null == session) {
912 // Shouldn't happen, but let's play nice...
913 if (debug >= 1) {
914 log("WARNING: can't set timout for null session " + sessionId);
915 }
916 return 0;
917 }
918 try {
919 int oldMaxInactiveInterval = session.getMaxInactiveInterval();
920 session.setMaxInactiveInterval(maxInactiveInterval);
921 return oldMaxInactiveInterval;
922 } catch (IllegalStateException ise) {
923 if (debug >= 1) {
924 log("Can't set MaxInactiveInterval '" + maxInactiveInterval + "' for invalidated session id " + sessionId);
925 }
926 return 0;
927 }
928 }
929
930 protected Comparator getComparator(String sortBy) {
931 Comparator comparator = null;
932 if ("CreationTime".equalsIgnoreCase(sortBy)) {
933 comparator = new BaseSessionComparator() {
934 public Comparable getComparableObject(Session session) {
935 return new Date(session.getCreationTime());
936 }
937 };
938 } else if ("id".equalsIgnoreCase(sortBy)) {
939 comparator = new BaseSessionComparator() {
940 public Comparable getComparableObject(Session session) {
941 return session.getId();
942 }
943 };
944 } else if ("LastAccessedTime".equalsIgnoreCase(sortBy)) {
945 comparator = new BaseSessionComparator() {
946 public Comparable getComparableObject(Session session) {
947 return new Date(session.getLastAccessedTime());
948 }
949 };
950 } else if ("MaxInactiveInterval".equalsIgnoreCase(sortBy)) {
951 comparator = new BaseSessionComparator() {
952 public Comparable getComparableObject(Session session) {
953 return new Date(session.getMaxInactiveInterval());
954 }
955 };
956 } else if ("new".equalsIgnoreCase(sortBy)) {
957 comparator = new BaseSessionComparator() {
958 public Comparable getComparableObject(Session session) {
959 return Boolean.valueOf(session.getSession().isNew());
960 }
961 };
962 } else if ("locale".equalsIgnoreCase(sortBy)) {
963 comparator = new BaseSessionComparator() {
964 public Comparable getComparableObject(Session session) {
965 return JspHelper.guessDisplayLocaleFromSession(session);
966 }
967 };
968 } else if ("user".equalsIgnoreCase(sortBy)) {
969 comparator = new BaseSessionComparator() {
970 public Comparable getComparableObject(Session session) {
971 return JspHelper.guessDisplayUserFromSession(session);
972 }
973 };
974 } else if ("UsedTime".equalsIgnoreCase(sortBy)) {
975 comparator = new BaseSessionComparator() {
976 public Comparable getComparableObject(Session session) {
977 return new Date(SessionUtils.getUsedTimeForSession(session));
978 }
979 };
980 } else if ("InactiveTime".equalsIgnoreCase(sortBy)) {
981 comparator = new BaseSessionComparator() {
982 public Comparable getComparableObject(Session session) {
983 return new Date(SessionUtils.getInactiveTimeForSession(session));
984 }
985 };
986 } else if ("TTL".equalsIgnoreCase(sortBy)) {
987 comparator = new BaseSessionComparator() {
988 public Comparable getComparableObject(Session session) {
989 return new Date(SessionUtils.getTTLForSession(session));
990 }
991 };
992 }
993 //TODO: complete this to TTL, etc.
994 return comparator;
995 }
996
997 // ------------------------------------------------------ Private Constants
998
999 // These HTML sections are broken in relatively small sections, because of
1000 // limited number of subsitutions MessageFormat can process
1001 // (maximium of 10).
1002
1003 private static final String APPS_HEADER_SECTION =
1004 "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
1005 "<tr>\n" +
1006 " <td colspan=\"5\" class=\"title\">{0}</td>\n" +
1007 "</tr>\n" +
1008 "<tr>\n" +
1009 " <td class=\"header-left\"><small>{1}</small></td>\n" +
1010 " <td class=\"header-left\"><small>{2}</small></td>\n" +
1011 " <td class=\"header-center\"><small>{3}</small></td>\n" +
1012 " <td class=\"header-center\"><small>{4}</small></td>\n" +
1013 " <td class=\"header-left\"><small>{5}</small></td>\n" +
1014 "</tr>\n";
1015
1016 private static final String APPS_ROW_DETAILS_SECTION =
1017 "<tr>\n" +
1018 " <td class=\"row-left\" bgcolor=\"{6}\" rowspan=\"2\"><small><a href=\"{0}\">{1}</a>" +
1019 "</small></td>\n" +
1020 " <td class=\"row-left\" bgcolor=\"{6}\" rowspan=\"2\"><small>{2}</small></td>\n" +
1021 " <td class=\"row-center\" bgcolor=\"{6}\" rowspan=\"2\"><small>{3}</small></td>\n" +
1022 " <td class=\"row-center\" bgcolor=\"{6}\" rowspan=\"2\">" +
1023 "<small><a href=\"{4}\" target=\"_blank\">{5}</a></small></td>\n";
1024
1025 private static final String MANAGER_APP_ROW_BUTTON_SECTION =
1026 " <td class=\"row-left\" bgcolor=\"{13}\">\n" +
1027 " <small>\n" +
1028 " {1} \n" +
1029 " {3} \n" +
1030 " {5} \n" +
1031 " {7} \n" +
1032 " </small>\n" +
1033 " </td>\n" +
1034 "</tr><tr>\n" +
1035 " <td class=\"row-left\" bgcolor=\"{13}\">\n" +
1036 " <form method=\"POST\" action=\"{8}\">\n" +
1037 " <small>\n" +
1038 " <input type=\"submit\" value=\"{9}\"> {10} <input type=\"text\" name=\"idle\" size=\"5\" value=\"{11}\"> {12} \n" +
1039 " </small>\n" +
1040 " </form>\n" +
1041 " </td>\n" +
1042 "</tr>\n";
1043
1044 private static final String STARTED_DEPLOYED_APPS_ROW_BUTTON_SECTION =
1045 " <td class=\"row-left\" bgcolor=\"{13}\">\n" +
1046 " <small>\n" +
1047 " {1} \n" +
1048 " <a href=\"{2}\" onclick=\"return(confirm('''Are you sure?'''))\">{3}</a> \n" +
1049 " <a href=\"{4}\" onclick=\"return(confirm('''Are you sure?'''))\">{5}</a> \n" +
1050 " <a href=\"{6}\" onclick=\"return(confirm('''Are you sure?'''))\">{7}</a> \n" +
1051 " </small>\n" +
1052 " </td>\n" +
1053 " </tr><tr>\n" +
1054 " <td class=\"row-left\" bgcolor=\"{13}\">\n" +
1055 " <form method=\"POST\" action=\"{8}\">\n" +
1056 " <small>\n" +
1057 " <input type=\"submit\" value=\"{9}\"> {10} <input type=\"text\" name=\"idle\" size=\"5\" value=\"{11}\"> {12} \n" +
1058 " </small>\n" +
1059 " </form>\n" +
1060 " </td>\n" +
1061 "</tr>\n";
1062
1063 private static final String STOPPED_DEPLOYED_APPS_ROW_BUTTON_SECTION =
1064 " <td class=\"row-left\" bgcolor=\"{13}\" rowspan=\"2\">\n" +
1065 " <small>\n" +
1066 " <a href=\"{0}\" onclick=\"return(confirm('''Are you sure?'''))\">{1}</a> \n" +
1067 " {3} \n" +
1068 " {5} \n" +
1069 " <a href=\"{6}\" onclick=\"return(confirm('''Are you sure? This will delete the application.'''))\">{7}</a> \n" +
1070 " </small>\n" +
1071 " </td>\n" +
1072 "</tr>\n<tr></tr>\n";
1073
1074 private static final String STARTED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION =
1075 " <td class=\"row-left\" bgcolor=\"{13}\" rowspan=\"2\">\n" +
1076 " <small>\n" +
1077 " {1} \n" +
1078 " <a href=\"{2}\" onclick=\"return(confirm('''Are you sure?'''))\">{3}</a> \n" +
1079 " <a href=\"{4}\" onclick=\"return(confirm('''Are you sure?'''))\">{5}</a> \n" +
1080 " {7} \n" +
1081 " </small>\n" +
1082 " </td>\n" +
1083 "</tr>\n<tr></tr>\n";
1084
1085 private static final String STOPPED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION =
1086 " <td class=\"row-left\" bgcolor=\"{13}\" rowspan=\"2\">\n" +
1087 " <small>\n" +
1088 " <a href=\"{0}\" onclick=\"return(confirm('''Are you sure?'''))\">{1}</a> \n" +
1089 " {3} \n" +
1090 " {5} \n" +
1091 " {7} \n" +
1092 " </small>\n" +
1093 " </td>\n" +
1094 "</tr>\n<tr></tr>\n";
1095
1096 private static final String DEPLOY_SECTION =
1097 "</table>\n" +
1098 "<br>\n" +
1099 "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
1100 "<tr>\n" +
1101 " <td colspan=\"2\" class=\"title\">{0}</td>\n" +
1102 "</tr>\n" +
1103 "<tr>\n" +
1104 " <td colspan=\"2\" class=\"header-left\"><small>{1}</small></td>\n" +
1105 "</tr>\n" +
1106 "<tr>\n" +
1107 " <td colspan=\"2\">\n" +
1108 "<form method=\"get\" action=\"{2}\">\n" +
1109 "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
1110 "<tr>\n" +
1111 " <td class=\"row-right\">\n" +
1112 " <small>{3}</small>\n" +
1113 " </td>\n" +
1114 " <td class=\"row-left\">\n" +
1115 " <input type=\"text\" name=\"deployPath\" size=\"20\">\n" +
1116 " </td>\n" +
1117 "</tr>\n" +
1118 "<tr>\n" +
1119 " <td class=\"row-right\">\n" +
1120 " <small>{4}</small>\n" +
1121 " </td>\n" +
1122 " <td class=\"row-left\">\n" +
1123 " <input type=\"text\" name=\"deployConfig\" size=\"20\">\n" +
1124 " </td>\n" +
1125 "</tr>\n" +
1126 "<tr>\n" +
1127 " <td class=\"row-right\">\n" +
1128 " <small>{5}</small>\n" +
1129 " </td>\n" +
1130 " <td class=\"row-left\">\n" +
1131 " <input type=\"text\" name=\"deployWar\" size=\"40\">\n" +
1132 " </td>\n" +
1133 "</tr>\n" +
1134 "<tr>\n" +
1135 " <td class=\"row-right\">\n" +
1136 " \n" +
1137 " </td>\n" +
1138 " <td class=\"row-left\">\n" +
1139 " <input type=\"submit\" value=\"{6}\">\n" +
1140 " </td>\n" +
1141 "</tr>\n" +
1142 "</table>\n" +
1143 "</form>\n" +
1144 "</td>\n" +
1145 "</tr>\n";
1146
1147 private static final String UPLOAD_SECTION =
1148 "<tr>\n" +
1149 " <td colspan=\"2\" class=\"header-left\"><small>{0}</small></td>\n" +
1150 "</tr>\n" +
1151 "<tr>\n" +
1152 " <td colspan=\"2\">\n" +
1153 "<form action=\"{1}\" method=\"post\" " +
1154 "enctype=\"multipart/form-data\">\n" +
1155 "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
1156 "<tr>\n" +
1157 " <td class=\"row-right\">\n" +
1158 " <small>{2}</small>\n" +
1159 " </td>\n" +
1160 " <td class=\"row-left\">\n" +
1161 " <input type=\"file\" name=\"deployWar\" size=\"40\">\n" +
1162 " </td>\n" +
1163 "</tr>\n" +
1164 "<tr>\n" +
1165 " <td class=\"row-right\">\n" +
1166 " \n" +
1167 " </td>\n" +
1168 " <td class=\"row-left\">\n" +
1169 " <input type=\"submit\" value=\"{3}\">\n" +
1170 " </td>\n" +
1171 "</tr>\n" +
1172 "</table>\n" +
1173 "</form>\n" +
1174 "</table>\n" +
1175 "<br>\n" +
1176 "\n";
1177
1178 private static final String DIAGNOSTICS_SECTION =
1179 "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
1180 "<tr>\n" +
1181 " <td colspan=\"2\" class=\"title\">{0}</td>\n" +
1182 "</tr>\n" +
1183 "<tr>\n" +
1184 " <td colspan=\"2\" class=\"header-left\"><small>{1}</small></td>\n" +
1185 "</tr>\n" +
1186 "<tr>\n" +
1187 " <td colspan=\"2\">\n" +
1188 "<form method=\"post\" action=\"{2}\">\n" +
1189 "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
1190 "<tr>\n" +
1191 " <td class=\"row-left\">\n" +
1192 " <input type=\"submit\" value=\"{4}\">\n" +
1193 " </td>\n" +
1194 " <td class=\"row-left\">\n" +
1195 " <small>{3}</small>\n" +
1196 " </td>\n" +
1197 "</tr>\n" +
1198 "</table>\n" +
1199 "</form>\n" +
1200 "</td>\n" +
1201 "</tr>\n" +
1202 "</table>\n" +
1203 "<br>";
1204
1205 }