001: /**
002: * Copyright (C) 2006, 2007 David Bulmore, Software Sensation Inc.
003: * All Rights Reserved.
004: *
005: * This file is part of jWebApp.
006: *
007: * jWebApp is free software; you can redistribute it and/or modify it under
008: * the terms of the GNU General Public License (Version 2) as published by
009: * the Free Software Foundation.
010: *
011: * jWebApp is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with jWebApp; if not, write to the Free Software Foundation,
018: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
019: */package jwebapp.controller;
020:
021: import jcommontk.utils.ExceptionUtils;
022: import java.util.logging.Level;
023: import java.util.logging.Logger;
024: import javax.servlet.ServletConfig;
025: import javax.servlet.ServletContext;
026: import javax.servlet.ServletException;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029: import jcommontk.utils.LoggerUtils;
030:
031: /**
032: * This is the main controller for jWebApp and should be called from any custom controllers.
033: */
034:
035: @SuppressWarnings("unchecked")
036: // working to complete a Java 1.5 version
037: public class WebAppController {
038: private static Logger logger = Logger
039: .getLogger(RequestServlet.class.getName());
040:
041: private InitDestroy initDestroyHandler;
042: private ServletContext servletContext;
043: private DataManager dataManager;
044: private String useDomainName, classPrefix = "webapp";
045: private boolean urlRewriting = true;
046:
047: static {
048: Logger logger = Logger.getLogger("jwebapp");
049:
050: logger.setUseParentHandlers(false);
051: logger.addHandler(new LoggerUtils.OutputStreamHandler(
052: System.out, true, true));
053: logger.setLevel(Level.FINER);
054: }
055:
056: /**
057: * Handles servlet initialization for jWebApp and should be called within the HttpServlet.init() method.
058: *
059: * @param config the servlet configuration instance
060: */
061:
062: public void init(ServletConfig config) throws ServletException {
063: String param = null;
064:
065: servletContext = config.getServletContext();
066:
067: useDomainName = config.getInitParameter("useDomainName");
068:
069: if ((param = config.getInitParameter("classPrefix")) != null)
070: classPrefix = param.trim();
071:
072: if ((param = config.getInitParameter("debug")) != null)
073: Logger.getLogger("jwebapp").setLevel(
074: Level.parse(param.trim().toUpperCase()));
075:
076: logger.config("debug level is "
077: + Logger.getLogger("jwebapp").getLevel());
078:
079: try {
080: if ((param = config.getInitParameter("urlRewriting")) != null)
081: urlRewriting = new Boolean(param).booleanValue();
082: } catch (Exception e) {
083: logger.log(Level.SEVERE, e.toString(), e);
084: }
085:
086: try {
087: if ((param = config.getInitParameter("initDestroyHandler")) != null)
088: initDestroyHandler = (InitDestroy) Class.forName(
089: param.trim()).newInstance();
090: else
091: initDestroyHandler = new InitDestroyHandler();
092:
093: initDestroyHandler.init(servletContext);
094: } catch (Exception e) {
095: logger.log(Level.SEVERE, e.toString(), e);
096: }
097:
098: try {
099: boolean validate = false;
100:
101: if ((param = config.getInitParameter("validateXml")) != null)
102: validate = new Boolean(param.trim()).booleanValue();
103:
104: if ((param = config.getInitParameter("configFile")) != null)
105: dataManager = new XMLDataManager(servletContext
106: .getResource(param.trim()), validate);
107: else
108: dataManager = new DataManager();
109: } catch (Exception e) {
110: logger.log(Level.SEVERE, e.toString(), e);
111:
112: throw new ServletException(e);
113: }
114: }
115:
116: /**
117: * Handles servlet destruction for jWebApp and should be called within the HttpServlet.destroy() method.
118: * This is only needed if the servlet has an InitDestroyHandler with a needed destroy method.
119: */
120:
121: public void destroy() {
122: try {
123: initDestroyHandler.destroy(servletContext);
124: } catch (Exception e) {
125: logger.log(Level.SEVERE, e.toString(), e);
126: }
127: }
128:
129: /**
130: * This is the main controller for jWebApp and all requests needing to be processed by jWebApp must call this method.
131: *
132: * @param request the HttpServletRequest request
133: * @param response the HttpServletResponse response
134: */
135:
136: protected void processRequest(HttpServletRequest request,
137: HttpServletResponse response) {
138: ServerInterface serverInterface = new ServerInterface(request,
139: response, servletContext, dataManager, useDomainName,
140: urlRewriting);
141: String queryString = request.getQueryString() == null ? ""
142: : "?" + request.getQueryString(), requestUri = request
143: .getRequestURI(), returnedUrlKey = null;
144: DataManager.RequestData requestData = null;
145: UrlData urlData = null;
146:
147: try {
148: if (logger.isLoggable(Level.FINE)) {
149: logger.fine("RequestURI = " + request.getRequestURI()
150: + ", RequestURL = " + request.getRequestURL());
151: logger.fine("request=" + requestUri);
152: }
153:
154: if (logger.isLoggable(Level.FINER)) {
155: logger.finer("request.getContextPath()="
156: + request.getContextPath());
157: logger.finer("request.getServletPath()="
158: + request.getServletPath());
159: logger.finer("request.getPathInfo()="
160: + request.getPathInfo());
161: logger.finer("request.getQueryString()="
162: + request.getQueryString());
163: }
164:
165: if (logger.isLoggable(Level.FINEST)) {
166: logger.finest("request.getPathTranslated()="
167: + request.getPathTranslated());
168: logger.finest("request.getAuthType()="
169: + request.getAuthType());
170: logger.finest("request.getContentType()="
171: + request.getContentType());
172: logger.finest("request.getMethod()="
173: + request.getMethod());
174: logger.finest("request.isSecure()="
175: + request.isSecure());
176: logger.finest("serverInterface.getRemoteUser()="
177: + serverInterface.getRemoteUser());
178: }
179:
180: if (requestUri.length() > 0
181: && (requestData = dataManager.getRequestData(
182: classPrefix, requestUri)) != null) {
183: response.setContentType("text/html");
184:
185: serverInterface.setRequestData(requestData);
186:
187: if (logger.isLoggable(Level.FINER)) {
188: logger.finer("request data for \"" + requestUri
189: + "\" found");
190: logger.finer("request is XML defined: "
191: + requestData.isDefined());
192: }
193:
194: if (serverInterface.hasPermission(requestData
195: .getRoleSet())) {
196: if (serverInterface.getRemoteUser() == null)
197: logger.finer("role permission not required");
198: else
199: logger
200: .finer("request is granted role permission");
201:
202: if (requestData.isSecure() && !request.isSecure()) {
203: logger
204: .finer("request requires a secure (https) connection");
205:
206: String msg = dataManager
207: .getMessage("notSecureMessage");
208:
209: if (msg == null)
210: msg = "Request can only be completed over a secure connection (i.e. https)!";
211:
212: serverInterface.setErrorMessage(
213: ServerInterface.NOT_SECURE_ERROR, msg);
214:
215: if ((urlData = dataManager
216: .getUrlData(requestData
217: .getUrlDataId(returnedUrlKey = RequestHandler.NOT_SECURE_ERROR))) == null) {
218: if (logger.isLoggable(Level.FINER))
219: logger
220: .finer("There is no URL information in path \""
221: + requestUri
222: + "\" for key \""
223: + returnedUrlKey + "\"");
224:
225: throw new ServletException(
226: msg != null ? msg
227: : "Request can only be completed over a secure connection (i.e. https)!");
228: }
229: } else {
230: if (request.isSecure())
231: logger
232: .finer("request is over a secure connection");
233: else
234: logger
235: .finer("request does not require a secure connection");
236:
237: RequestHandler handler = requestData
238: .getRequestHandler();
239:
240: if (handler == null) {
241: if (logger.isLoggable(Level.FINER))
242: logger
243: .finer("Request handler class not defined for request "
244: + requestData.getId());
245:
246: if ((urlData = dataManager
247: .getUrlData(requestData
248: .getUrlDataId(returnedUrlKey = RequestHandler.SUCCESS))) == null) {
249: if (logger.isLoggable(Level.FINER))
250: logger
251: .finer("There is no URL information in path \""
252: + requestUri
253: + "\" for key \""
254: + returnedUrlKey
255: + "\"");
256:
257: urlData = dataManager
258: .getUrlData(returnedUrlKey = "home");
259: }
260: } else {
261: if (logger.isLoggable(Level.FINER))
262: logger.finer("Request handler \""
263: + requestData
264: .getHandlerClassName()
265: + "\" found");
266:
267: Object rval = null;
268:
269: serverInterface
270: .setReturnedUrlKey(returnedUrlKey = RequestHandler.SUCCESS);
271:
272: if (requestData.isDefined())
273: if (handler.validateParameters()) {
274: serverInterface
275: .setReturnedUrlKey(returnedUrlKey = serverInterface
276: .validateParameters() ? RequestHandler.SUCCESS
277: : RequestHandler.VALIDATION_ERROR);
278:
279: if (logger.isLoggable(Level.FINER))
280: logger
281: .finer("parameter validation returned \""
282: + returnedUrlKey
283: + "\"");
284: }
285:
286: if (returnedUrlKey
287: .equalsIgnoreCase(RequestHandler.SUCCESS))
288: if ((rval = validate(serverInterface,
289: requestData)) != null) {
290: serverInterface
291: .setReturnedUrlKey(returnedUrlKey = (String) rval);
292:
293: if (logger.isLoggable(Level.FINER))
294: logger
295: .finer("request validation returned \""
296: + returnedUrlKey
297: + "\"");
298: }
299:
300: if (returnedUrlKey
301: .equalsIgnoreCase(RequestHandler.SUCCESS))
302: if ((rval = process(serverInterface,
303: requestData)) != null) {
304: serverInterface
305: .setReturnedUrlKey(returnedUrlKey = (String) rval);
306:
307: if (logger.isLoggable(Level.FINER))
308: logger
309: .finer("request processing returned \""
310: + returnedUrlKey
311: + "\"");
312: } else
313: serverInterface
314: .setReturnedUrlKey(returnedUrlKey = RequestHandler.NO_FORWARDING);
315:
316: if (!returnedUrlKey
317: .equalsIgnoreCase(RequestHandler.NO_FORWARDING))
318: if (requestData.isDefined()) {
319: urlData = dataManager
320: .getUrlData(requestData
321: .getUrlDataId(returnedUrlKey));
322:
323: if (urlData == null)
324: urlData = new UrlData(
325: "No Id Defined",
326: returnedUrlKey);
327: } else
328: urlData = new UrlData(
329: "No Id Defined",
330: returnedUrlKey);
331: }
332: }
333: } else {
334: logger.finer("User not authenticated");
335:
336: if ((urlData = dataManager
337: .getUrlData(requestData
338: .getUrlDataId(returnedUrlKey = RequestHandler.AUTHENTICATION_ERROR))) == null)
339: if (logger.isLoggable(Level.FINER))
340: logger
341: .finer("There is no URL information in path \""
342: + requestUri
343: + "\" for key \""
344: + returnedUrlKey + "\"");
345:
346: if (serverInterface.getRemoteUser() == null) {
347: String msg = dataManager
348: .getMessage("loginMessage");
349:
350: if (msg == null)
351: msg = "Please login, or create a new login account";
352:
353: serverInterface.setErrorMessage(
354: ServerInterface.AUTHENTICATION_ERROR,
355: msg);
356:
357: if (urlData == null)
358: throw new UserSecurityException(msg);
359: } else {
360: String msg = dataManager
361: .getMessage("permissionMessage");
362:
363: if (msg == null)
364: msg = "Must have role level permission to access this page";
365:
366: serverInterface.setErrorMessage(
367: ServerInterface.AUTHENTICATION_ERROR,
368: msg);
369:
370: if (urlData == null)
371: throw new UserSecurityException(msg);
372: }
373: }
374:
375: if (logger.isLoggable(Level.FINER))
376: logger.finer("forwarding key=" + returnedUrlKey);
377:
378: if (!returnedUrlKey
379: .equalsIgnoreCase(RequestHandler.NO_FORWARDING))
380: handleUrlData(serverInterface, requestData,
381: urlData, returnedUrlKey);
382: } else {
383: if (request.getPathInfo() != null)
384: serverInterface.forwardRequest(request
385: .getPathInfo());
386: else {
387: if (logger.isLoggable(Level.FINER))
388: logger.finer("request data for \"" + requestUri
389: + "\" was not found");
390:
391: response.sendError(
392: HttpServletResponse.SC_NOT_FOUND,
393: "request data for \"" + requestUri
394: + queryString + "\" was not found");
395: }
396: }
397: } catch (UserSecurityException e) {
398: logger.log(Level.FINE, "UserSecurityException: " + e, e);
399:
400: serverInterface.setErrorMessage(
401: ServerInterface.AUTHENTICATION_ERROR, e
402: .getMessage());
403: serverInterface.setException(e);
404:
405: if (serverInterface.getRemoteUser() == null)
406: urlData = dataManager
407: .getUrlData(returnedUrlKey = "login");
408: else
409: urlData = dataManager
410: .getUrlData(returnedUrlKey = "error");
411:
412: try {
413: handleUrlData(serverInterface, requestData, urlData,
414: returnedUrlKey);
415: } catch (Exception e2) {
416: logger.log(Level.SEVERE, e2.toString(), e2);
417: }
418: } catch (Exception e) {
419: logger.log(Level.SEVERE, e.toString(), e);
420:
421: serverInterface.setErrorMessage(ExceptionUtils
422: .firstMessage(e));
423: serverInterface.setException(e);
424:
425: if ((urlData = dataManager
426: .getUrlData(returnedUrlKey = "error")) == null)
427: urlData = new UrlData("No Id Defined",
428: "/WEB-INF/error.jsp");
429:
430: try {
431: handleUrlData(serverInterface, requestData, urlData,
432: returnedUrlKey);
433: } catch (Exception e2) {
434: logger.log(Level.SEVERE, e2.toString(), e2);
435: }
436: }
437: }
438:
439: String validate(ServerInterface serverInterface,
440: DataManager.RequestData requestData)
441: throws JWebAppException {
442: try {
443: if (requestData.getValidationMethod() != null)
444: return (String) requestData
445: .getValidationMethod()
446: .invoke(
447: requestData.getRequestHandler(),
448: getParameters(
449: serverInterface,
450: requestData
451: .getValidationMethodType()));
452: } catch (Exception e) {
453: throw new JWebAppException(e);
454: }
455:
456: return RequestHandler.SUCCESS;
457: }
458:
459: String process(ServerInterface serverInterface,
460: DataManager.RequestData requestData)
461: throws JWebAppException {
462: try {
463: return (String) requestData.getProcessingMethod().invoke(
464: requestData.getRequestHandler(),
465: getParameters(serverInterface, requestData
466: .getProcessingMethodType()));
467: } catch (Exception e) {
468: throw new JWebAppException(e);
469: }
470: }
471:
472: Object[] getParameters(ServerInterface serverInterface,
473: int methodType) {
474: switch (methodType) {
475: case DataManager.RequestData.PARAM_SERVER_INTERFACE:
476: return new Object[] { serverInterface };
477: case DataManager.RequestData.PARAM_SERVLET_REQUEST:
478: return new Object[] { serverInterface.getServletRequest() };
479: case DataManager.RequestData.PARAM_SERVLET_RESPONSE:
480: return new Object[] { serverInterface.getServletResponse() };
481: case DataManager.RequestData.PARAM_SERVLET_BOTH:
482: return new Object[] { serverInterface.getServletRequest(),
483: serverInterface.getServletResponse() };
484: default:
485: return new Object[] { null };
486: }
487: }
488:
489: void handleUrlData(ServerInterface serverInterface,
490: DataManager.RequestData requestData, UrlData forwardData,
491: String forwardKey) throws ServletException {
492: if (forwardData == null) {
493: if (logger.isLoggable(Level.FINER))
494: logger.finer("There is no URL information in path \""
495: + requestData.getId() + "\" for key \""
496: + forwardKey + "\"");
497:
498: throw new ServletException(
499: "There is no URL information in path \""
500: + requestData.getId() + "\" for key \""
501: + forwardKey + "\"");
502: }
503:
504: if (logger.isLoggable(Level.FINE))
505: logger.fine("forwarding to \"" + forwardData.getUrl()
506: + "\"");
507:
508: if (forwardData.isRedirect()) {
509: logger.finer("is redirect");
510:
511: redirect(forwardData, serverInterface);
512: } else {
513: forward(forwardData, serverInterface);
514: }
515: }
516:
517: void redirect(UrlData forwardData, ServerInterface serverInterface) {
518: try {
519: serverInterface.redirectRequest(serverInterface
520: .getBuiltUrl(forwardData, true, true, true));
521: } catch (Exception e) {
522: logger.log(Level.SEVERE, e.toString(), e);
523:
524: try {
525: serverInterface.getServletResponse().sendError(
526: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
527: e.toString());
528: } catch (Exception e2) {
529: logger.log(Level.SEVERE, e2.toString(), e2);
530: }
531: }
532: }
533:
534: void forward(UrlData forwardData, ServerInterface serverInterface) {
535: try {
536: if (logger.isLoggable(Level.FINEST)) {
537: logger.finest("errorCode: "
538: + serverInterface.getErrorCode());
539: logger.finest("errorMessage: "
540: + serverInterface.getErrorMessage());
541: logger.finest("warningCode: "
542: + serverInterface.getWarningCode());
543: logger.finest("warningMessage: "
544: + serverInterface.getWarningMessage());
545: logger.finest("applicationCode: "
546: + serverInterface.getApplicationCode());
547: logger.finest("applicationMessage: "
548: + serverInterface.getApplicationMessage());
549: }
550:
551: serverInterface.setAttribute("jwaUrl", serverInterface
552: .getUrlDataWrapper());
553: serverInterface.setAttribute("jwaMessage", dataManager
554: .getMessageHash());
555: serverInterface.setAttribute("jwaContextPath",
556: serverInterface.getServletRequest()
557: .getContextPath());
558: serverInterface.setAttribute("jwaServletPath",
559: serverInterface.getServletRequest()
560: .getServletPath());
561:
562: serverInterface.forwardRequest(serverInterface
563: .resolveForwardReferences(forwardData));
564: } catch (Exception e) {
565: logger.log(Level.SEVERE, e.toString(), e);
566:
567: try {
568: serverInterface.getServletResponse().sendError(
569: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
570: e.toString());
571: } catch (Exception e2) {
572: logger.log(Level.SEVERE, e2.toString(), e2);
573: }
574: }
575: }
576:
577: static class UserSecurityException extends Exception {
578: private static final long serialVersionUID = 100L;
579:
580: public UserSecurityException() {
581: super ();
582: }
583:
584: public UserSecurityException(String message) {
585: super (message);
586: }
587:
588: public UserSecurityException(String message, Throwable cause) {
589: super (message, cause);
590: }
591:
592: public UserSecurityException(Throwable cause) {
593: super(cause);
594: }
595: }
596: }
|