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