001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: HttpPresentationServlet.java,v 1.4 2007-10-19 10:05:39 sinisa Exp $
022: */
023:
024: package com.lutris.appserver.server.httpPresentation.servlet;
025:
026: import java.io.File;
027: import java.io.IOException;
028: import java.io.PrintWriter;
029: import java.lang.reflect.Constructor;
030: import java.util.regex.Pattern;
031:
032: import javax.servlet.ServletConfig;
033: import javax.servlet.ServletContext;
034: import javax.servlet.ServletException;
035: import javax.servlet.ServletRequest;
036: import javax.servlet.http.HttpServlet;
037: import javax.servlet.http.HttpServletRequest;
038: import javax.servlet.http.HttpServletResponse;
039:
040: import org.apache.axis.transport.http.AxisServlet;
041: import org.enhydra.server.EnhydraServer;
042: import org.enhydra.util.ConfigFileInterface;
043: import org.enhydra.util.ServiceExtractor;
044:
045: import com.lutris.appserver.server.Application;
046: import com.lutris.appserver.server.ApplicationException;
047: import com.lutris.appserver.server.Enhydra;
048: import com.lutris.appserver.server.StandardApplication;
049: import com.lutris.appserver.server.httpPresentation.HttpPresentationException;
050: import com.lutris.appserver.server.httpPresentation.HttpPresentationManager;
051: import com.lutris.appserver.server.httpPresentation.HttpPresentationRequest;
052: import com.lutris.appserver.server.httpPresentation.HttpPresentationResponse;
053: import com.lutris.appserver.server.session.Session;
054: import com.lutris.classloader.MultiClassLoader;
055: import com.lutris.logging.LogChannel;
056: import com.lutris.logging.Logger;
057: import com.lutris.util.Config;
058:
059: /**
060: * Presentation server implemented as a servlet. Translates servlet
061: * requests into HttpPresentationManager requests. There is a one
062: * to one correspondence between an instance of this servlet and
063: * an Enhydra application. An instance of the application is created by
064: * the servlet.
065: *
066: * The servlet requires a single init parameter `configFile', that
067: * contains the path to the application's configuration file. This
068: * file must define the following fields:
069: * <UL>
070: * <LI> <CODE>server.classPath</CODE> - The class path for the application,
071: * as a comma separated list. This should not contain the class path
072: * for the Java runtime classes or the Lutris classes.
073: * <LI> <CODE>server.appClass</CODE> - The full class name of the application
074: * class. This must implement
075: * <CODE>com.lutris.appserver.server.Application</CODE>.
076: * <LI> <CODE>server.presentationPrefix</CODE> - This is the path that is used as
077: * a prefixed for all URL references within the application. It must use
078: * a `/' separator (as with URLs).
079: * <LI> <CODE>server.autoReload</CODE> - A flag to indicate whether or not
080: * the application should be automatically reloaded when any class or
081: * jar file changes in the application's classpath. <B><I>NOTE: THIS IS
082: * A DEBUGGING OPTION AND MAY SLOW DOWN THE APPLICATION!</I></B>
083: * <LI>
084: * </UL> <P>
085: *
086: * This servlet also implements the <CODE>ListenableServlet</CODE>
087: * interface. This allows other objects to register with this servlet and
088: * be notified every time <CODE>service</CODE> is called. These remote
089: * listeners may ignore the notification, examine the request, or even
090: * replace the request and/or response with extended versions. This is
091: * used by the debugging and monitoring servlets.
092: *
093: *
094: * @version $Revision: 1.4 $
095: * @author Mark Diekhans
096: * @author Damir Milovic
097: * @author Strahinja Videnovic
098: * @author Slobodan Vujasinovic
099: * @since 1.3
100: */
101: //FIXME: Explain the presentationPrefix better; give example.
102: //FIXME: Integrate URLEncodeServlet
103: public class HttpPresentationServlet extends HttpServlet {
104:
105: /**
106: * Log level for DOM statistics.
107: */
108: public static final String XMLC_DOM_STATS_LOG_LEVEL = "XMLC_DOM_STATS";
109:
110: /**
111: * Servlet is started under this context
112: */
113: protected ServletContext context;
114:
115: /*
116: * Application configuration definitions.
117: */
118: private static final String APP_CONFIG_INIT_PARAM_NAME = EnhydraServer.CONF_FILE;
119:
120: private static final String APP_CONFIG_FILE_CLASS = EnhydraServer.CONF_FILE_CLASS; // TJ 08.11.2003.
121:
122: private static final String APP_CLASS_PATH_FIELD = EnhydraServer.SERVER
123: + "." + EnhydraServer.CLASS_PATH;
124:
125: private static final String APP_APP_CLASS_FIELD = EnhydraServer.SERVER
126: + "." + EnhydraServer.APP_CLASS;
127:
128: private static final String APP_PRESENTATION_PREFIX_FIELD = EnhydraServer.SERVER
129: + "." + EnhydraServer.PRESENTATION_PREFIX;
130:
131: private static final String APP_AUTO_RELOAD_FIELD = EnhydraServer.SERVER
132: + "." + EnhydraServer.AUTO_RELOAD;
133:
134: // private static final String APP_DEFERRED_PARSING_FIELD = EnhydraServer.SERVER
135: // + ".XMLC.DeferredParsing"; //SV
136:
137: private static final String APP_JIVAN_RELOAD_FIELD = EnhydraServer.SERVER
138: + ".AutoReload"; //vr 16.05.2004.
139:
140: private static final String APP_ENCODING_TYPE = EnhydraServer.APPLICATION
141: + "." + "Encoding";
142:
143: private static final String APP_PRESENTATION_TOOLS = EnhydraServer.APPLICATION
144: + ".PresentationTools"; //vr 22.06.2004.
145:
146: /*
147: * For logging info about starting applications
148: */
149: private LogChannel logChannel;
150:
151: /*
152: * If we create the central logger, we save a copy of it for later
153: * initialization. If we didn't create it, this will be null.
154: */
155: private Logger standardLogger;
156:
157: /*
158: * If we create the central logger, we will need to initialize it later
159: * when init() is called.
160: */
161: private boolean needToConfigureLogChannel = true;
162:
163: /*
164: * Specify character encoding which will be used by server to encode form data.
165: */
166: String characterEncoding;
167:
168: private boolean needToSetCharacterEncoding = false;
169:
170: /*
171: * Application and its configuration information.
172: */
173: Application application = null;
174:
175: Config appConfig = null;
176:
177: String presentationPrefix;
178:
179: String logClassName;
180:
181: boolean cacheClasses;
182:
183: boolean cacheFiles;
184:
185: boolean autoReload; // Reload application when its classes are modified?
186:
187: HttpPresentationManager presentationManager = null;
188:
189: // private boolean isMemoryPersistence = false; // MemoryPersistence support
190:
191: // private String appName = null; // MemoryPersistence support
192:
193: /**
194: * If not null, log DOM statistics here after each DOM write.
195: */
196: private PrintWriter domStatsLogWriter;
197:
198: private AxisServlet axisServlet = null;
199:
200: //dt 301105 start
201: //private Pattern axisPattern = Pattern.compile(".*Axis.*");
202: private Pattern servicesPattern = null;
203:
204: // dt 301105 end
205:
206: private MultiClassLoader appClassLoader = null; //vr
207:
208: /**
209: * Create a new HttpPresentationServlet.
210: */
211: public HttpPresentationServlet() {
212: super ();
213: }
214:
215: /**
216: * Throw a servlet exception logging the message if possible.
217: */
218: private void throwServletException(String msg, Throwable cause)
219: throws ServletException {
220: if (logChannel != null) {
221: logChannel.write(Logger.ERROR, msg, cause);
222: } else {
223: System.err.println("Error: " + msg);
224: cause.printStackTrace();
225: }
226: throw new ServletException(msg + "\ncaused by "
227: + cause.getClass() + ": " + cause.getMessage());
228: }
229:
230: /**
231: * Generate an error for a field not being specified in a config file.
232: */
233: private void missingConfField(String field, String file)
234: throws HttpPresentationException {
235: throw new HttpPresentationException("field \"" + field
236: + "\" not specified in application config file \""
237: + file + "\"");
238: }
239:
240: /**
241: * Open and read the application's configuration file and initialize the
242: * application
243: */
244:
245: private String initConfig(ServletConfig servletConfig)
246: throws ServletException {
247:
248: try {
249: // Get configuration file and parameters.
250: String configFileName = servletConfig
251: .getInitParameter(APP_CONFIG_INIT_PARAM_NAME);
252: if (configFileName == null)
253: configFileName = EnhydraServer.DEFAULT_CONF_FILE;
254:
255: // if confgFile is relative, make it absolute
256: // TJ 08.11.2003 begin
257: String classDefinition = servletConfig
258: .getInitParameter(APP_CONFIG_FILE_CLASS);
259: if (classDefinition == null)
260: classDefinition = EnhydraServer.DEFAULT_CONF_FILE_CLASS;
261: Class imp = null;
262: try {
263: imp = Class.forName(classDefinition);
264: } catch (Exception ex) {
265: try {
266: classDefinition = EnhydraServer.DEFAULT_CONF_FILE_CLASS;
267: imp = Class.forName(classDefinition);
268: } catch (Exception e) {
269: throw new ServletException(
270: "Can not create configFile");
271: }
272: }
273:
274: Class[] classParam = new Class[1];
275: classParam[0] = Class.forName("java.io.File");
276: Constructor constr = imp.getConstructor(classParam);
277: Object[] arguments = new Object[1];
278: File configFile = new File(configFileName);
279: String contextUrl = this .getServletConfig()
280: .getServletContext().getRealPath("");
281: if (!configFile.isAbsolute()
282: && !configFileName.startsWith("/")) { /*for Windows OS check */
283: configFile = new File(contextUrl, "WEB-INF/"
284: + configFileName);
285: }
286: arguments[0] = (Object) (configFile);
287: ConfigFileInterface configF = (ConfigFileInterface) constr
288: .newInstance(arguments);
289: appConfig = configF.getConfig();
290:
291: return configFileName;
292: } catch (Exception except) {
293: throwServletException(
294: "Initialization of application failed", except);
295: }
296: return null;
297: }
298:
299: /**
300: * Open and read the application's configuration file and initialize the
301: * application, but don't start it yet. This is done on in service so it
302: * part of the standard state-change mechanism.
303: */
304: private void initApplication(ServletConfig servletConfig)
305: throws ServletException {
306: try {
307: String configFileName = servletConfig
308: .getInitParameter(APP_CONFIG_INIT_PARAM_NAME);
309: if (configFileName == null)
310: configFileName = EnhydraServer.DEFAULT_CONF_FILE;
311: String contextUrl = this .getServletConfig()
312: .getServletContext().getRealPath("");
313:
314: //adds all archive files from lib into classloader classpath
315: File libDir = new File(contextUrl, "WEB-INF/lib");
316: File[] archives = libDir.listFiles(new ArchiveFilter());
317: if (archives != null) {
318: for (int i = 0; i < archives.length; i++) {
319: this .appClassLoader.addClassPath(archives[i]);
320: }
321: }
322:
323: //add classes dir into claspath
324: this .appClassLoader.addClassPath(new File(contextUrl,
325: "WEB-INF/classes"));
326: // Add application's class path from config file into classloader.
327: String[] appClassPath = appConfig.getStrings(
328: APP_CLASS_PATH_FIELD, null);
329: if (appClassPath != null)
330: this .appClassLoader.addClassPath(appClassPath);
331:
332: // for MemoryPersistence support, we need key
333: String appClass = appConfig.getString(APP_APP_CLASS_FIELD);
334: String name = appClass;
335: /* int idxn = name.lastIndexOf('.');
336: if (idxn >= 0)
337: name = name.substring(idxn + 1); //application name from Class name
338: //do not save ClassLoader if MemoryPersistence = false
339: this.appName = name;
340: isMemoryPersistence = false;
341: if (appConfig.containsKey("SessionManager.MemoryPersistence"))
342: isMemoryPersistence = appConfig.getBoolean("SessionManager.MemoryPersistence");
343: if (isMemoryPersistence) {
344: ClassLoader cl = MemoryPersistence.getClassLoader(name);
345: if (cl == null) {
346: MemoryPersistence.setClassLoader(name, appClassLoader);
347: }
348: else {
349: appClassLoader = (MultiClassLoader) cl;
350: }
351: }
352: */
353: if (appClass == null) {
354: missingConfField(APP_APP_CLASS_FIELD, configFileName);
355: }
356: presentationPrefix = appConfig
357: .getString(APP_PRESENTATION_PREFIX_FIELD);
358: if (presentationPrefix == null) {
359: missingConfField(APP_PRESENTATION_PREFIX_FIELD,
360: configFileName);
361: }
362: autoReload = appConfig.getBoolean(APP_AUTO_RELOAD_FIELD,
363: false);
364:
365: characterEncoding = appConfig.getString(APP_ENCODING_TYPE,
366: null);
367: if (characterEncoding != null) {
368: needToSetCharacterEncoding = true;
369: }
370:
371: // Config of presentation manager
372: cacheClasses = appConfig.getBoolean(
373: "PresentationManager.CacheClasses", true);
374: cacheFiles = appConfig.getBoolean(
375: "PresentationManager.CacheFiles", false);
376:
377: logChannel.write(Logger.DEBUG,
378: "Loaded Enhydra application: " + appClass);
379: logChannel.write(Logger.DEBUG, " presentation prefix: "
380: + presentationPrefix);
381:
382: // Load actual application object.
383: Class appClassObj = appClassLoader.loadClass(appClass);
384: application = (Application) appClassObj.newInstance();
385:
386: /*
387: * Register the application object with this thread.
388: * We do this so that all calls into the Application object
389: * can count the Enhydra class to successfully return the
390: * Application object.
391: */
392: Enhydra.register(application);
393:
394: try {
395: // vr application.setLogChannel(Logger.getCentralLogger().getChannel(name));
396: application.setLogChannel(standardLogger
397: .getChannel(name)); // vr
398:
399: // FIXME: need to load app logger configuration from app conf file
400: application.setName(name);
401: } finally {
402: // Be sure the thread is unregistered no matter what.
403: Enhydra.unRegister();
404: }
405: setPresentationManager(context, appClassLoader);
406:
407: // vr 22.06.2004 begin
408: // reads list of available presentation tools
409: String[] presTools = appConfig.getStrings(
410: this .APP_PRESENTATION_TOOLS,
411: new String[] { "xmlc" });
412: for (int i = 0; i < presTools.length; i++) {
413:
414: if (presTools[i].equalsIgnoreCase("xmlc")) {
415: // sets XMLC Factory (needs classLoader set).
416: application.setXMLCFactory();
417: } else if (presTools[i].equalsIgnoreCase("jivan")) {
418: // sets Jivan Factory.
419: boolean jivanAutoReload = appConfig.getBoolean(
420: this .APP_JIVAN_RELOAD_FIELD, false);
421: application.setJivanFactory(jivanAutoReload);
422: }
423: }
424: // vr 22.06.2004 end
425:
426: LogChannel appLogChannel = application.getLogChannel();
427: if (appLogChannel != null) {
428: if (appLogChannel.isEnabled(XMLC_DOM_STATS_LOG_LEVEL)) {
429: domStatsLogWriter = appLogChannel
430: .getLogWriter(XMLC_DOM_STATS_LOG_LEVEL);
431: }
432: }
433:
434: // Tell the presentation manager about additional
435: // extension/mime-type mappings.
436: Config mimeTypes = appConfig.getConfig("Server.MimeType");
437: if (mimeTypes != null) {
438: String extensions[] = mimeTypes.keys();
439: for (int idx = 0; idx < extensions.length; idx++) {
440: presentationManager.addMimeType(mimeTypes
441: .getString(extensions[idx]),
442: extensions[idx]);
443: }
444: }
445: } catch (Exception except) {
446: throwServletException(
447: "Initialization of application failed", except);
448: }
449: }
450:
451: /*
452: * Standard servlet init function, creates the presentation manager
453: * we are going to use.
454: *
455: * @param config Object containing the servlet's startup
456: * configuration and initialization parameters.
457: * @exception ServletException if a servlet exception has occurred.
458: */
459: public synchronized void init(ServletConfig config)
460: throws ServletException {
461: super .init(config); // You must do this when overriding this method.
462:
463: context = config.getServletContext();
464: //SM 11.05.2004.
465: initConfig(config);
466:
467: ClassLoader secondaryLoader = Thread.currentThread()
468: .getContextClassLoader(); //vr
469: //this.appClassLoader = new MultiClassLoader(secondaryLoader.getParent(), secondaryLoader, logChannel); //vr
470: this .appClassLoader = new MultiClassLoader(secondaryLoader,
471: null, logChannel); //vr
472: appClassLoader.forceSecondaryLoader(true);
473: appClassLoader.enableAutoReloadForSecLoader(this .autoReload);
474:
475: try {
476: logClassName = appConfig.getString(EnhydraServer.LOG_CLASS,
477: EnhydraServer.DEFAULT_LOG_CLASS);
478: if (logClassName == null)
479: logClassName = EnhydraServer.DEFAULT_LOG_CLASS;
480: //vr standardLogger = (Logger) Class.forName(logClassName).getConstructor(new
481: standardLogger = (Logger) Class.forName(logClassName, true,
482: appClassLoader).getConstructor(
483: new Class[] { Boolean.TYPE }).newInstance(
484: new Object[] { Boolean.valueOf(true) });
485: if (needToConfigureLogChannel) {
486: standardLogger.configure(appConfig);
487: needToConfigureLogChannel = false;
488: }
489: } catch (Exception e) {
490: throwServletException("Initialization of logger failed", e);
491: }
492: logChannel = standardLogger.getChannel("Enhydra");
493:
494: initApplication(config);
495: // dt 291105 start
496: // extract Axis service names from server-config.wsdd and form a pattern
497: // to recognize Axis requests
498: String contextUrl = this .getServletConfig().getServletContext()
499: .getRealPath("");
500: ServiceExtractor serviceExtractor = new ServiceExtractor(
501: contextUrl + "/WEB-INF/server-config.wsdd", new File(
502: contextUrl).getName());
503: servicesPattern = serviceExtractor.getPattern();
504: //System.out.println(serviceExtractor);
505: // dt 291105 end
506:
507: // This call is added for the 3.0 release.
508: // in the past ensure app is running was called by the servlet-manager
509: // after a cast of the servlet variable.
510: ensureAppIsRunning();
511: /**
512: * register servlet on startup to EnhydraServer
513: * (need for admin application)
514: */
515: EnhydraServer es = EnhydraServer.getInstance();
516: es.register(this );
517: }
518:
519: /**
520: * Sets the presentation manager for the application. First creates a
521: * new presentation manager with the presentationPrefix, application,
522: * cacheClasses and cacheFiles fields (so these parameters must already
523: * be initialized). Then, the presentation manager is set in the
524: * application.
525: *
526: * @param context The servlet context for this application.
527: * @param appClassLoader The class loader for this application.
528: * @exception ServletException if a servlet exception has occurred.
529: */
530: private void setPresentationManager(ServletContext context,
531: ClassLoader appClassLoader) throws ServletException {
532: try {
533: presentationManager = new HttpPresentationManager(
534: presentationPrefix, application, appClassLoader,
535: cacheClasses, cacheFiles);
536: presentationManager.setServletAndContext(this , context);
537: } catch (Exception except) {
538: throwServletException(
539: "Initialization of presentation manager failed",
540: except);
541: }
542:
543: /*
544: * Tell application about the presentation manager running it.
545: *
546: * Register the application object with this thread.
547: * We do this so that all calls into the Application object
548: * can count the Enhydra class to successfully return the
549: * Application object.
550: */
551: Enhydra.register(application);
552: try {
553: application.setHttpPresentationManager(presentationManager);
554: } finally {
555: // Be sure the thread is unregistered no matter what.
556: Enhydra.unRegister();
557: }
558: }
559:
560: /**
561: * Attempt to change the application to the running state.
562: */
563: private synchronized void changeToRunningState()
564: throws ApplicationException {
565:
566: // We may or may not be registered here..
567: boolean isRegistered = (Enhydra.getApplication() != null);
568: if (!isRegistered) {
569: Enhydra.register(application);
570: }
571: try {
572: switch (application.getState()) {
573: case Application.STOPPED:
574: application.startup(appConfig);
575: try {
576: if (application instanceof StandardApplication) {
577: ((StandardApplication) application)
578: .initDodsAsyncCaches();
579: if (logChannel != null)
580: logChannel.write(Logger.DEBUG,
581: "Asynchronous DODS cache load for the application "
582: + application.getName()
583: + " is initialized!");
584: }
585: } catch (Exception e) {
586: e.printStackTrace();
587: if (logChannel != null)
588: logChannel.write(Logger.DEBUG,
589: "Asynchronous DODS cache load for the application "
590: + application.getName()
591: + " isn't initialized!");
592: }
593: isRegistered = (Enhydra.getApplication() != null);
594: break;
595: case Application.RUNNING:
596: break;
597: case Application.INCOMPLETE:
598: application.restartup(appConfig);
599: break;
600: case Application.DEAD:
601: throw new ApplicationException("The application "
602: + application.getName() + " is dead");
603: case Application.HALTED:
604: throw new ApplicationException("The application "
605: + application.getName() + " is halted");
606:
607: default:
608: throw new ApplicationException("The application "
609: + application.getName() + " in invalid state");
610: }
611: } finally {
612: if (!isRegistered) {
613: Enhydra.unRegister();
614: }
615: }
616: }
617:
618: /**
619: * This is an HTTP-specific version of the Servlet.service method
620: * that translates that request into a request to a presentation
621: * manager. This object is method is not synchronized, the thread
622: * is used to handle to entire request. <P>
623: *
624: * As part of implementing the FiterableServlet interface, the real work
625: * is done here in serviceDirect(), while calls to service() result in
626: * filters being applied and called. The end of the filter chain is a
627: * glue servlet that directly calls this method.
628: *
629: * @param req encapsulates the request to the servlet
630: * @param resp encapsulates the response from the servlet
631: * @exception ServletException if the request could not be handled
632: * @exception IOException if detected when handling the request
633: */
634: public void serviceDirect(HttpServletRequest req,
635: HttpServletResponse resp) throws ServletException,
636: IOException {
637:
638: /*
639: * Register the application object with this thread.
640: * We do this so that all the calls into the Application object
641: * can count on the Enhydra class to return the Application object.
642: * This is important not so much for the Application object, but
643: * for any utility classes it may call.
644: */
645: Enhydra.register(application);
646: try {
647: ensureAppIsRunning();
648:
649: if (needToSetCharacterEncoding)
650: req.setCharacterEncoding(characterEncoding);
651:
652: // Is this axis request
653: String soapAction = null;
654: boolean wsdl = false;
655:
656: // Preparation for CheckAxisSOAPAction and CheckAxisWsdlQery parameters
657: // Optional PO exclusion from those parameter checks
658:
659: //if (!presentationManager.isPresentationRequest(getHttpPresentationRequest(req,resp))) {
660: if (true) {
661: soapAction = req.getHeader("SOAPAction");
662: String query = req.getQueryString();
663:
664: if ("wsdl".equals(query)) {
665: wsdl = true;
666: logChannel
667: .write(Logger.DEBUG,
668: "It is an Axis request for a wsdl service description");
669: }
670: }
671:
672: if (soapAction != null
673: || wsdl
674: || ((servicesPattern != null) && servicesPattern
675: .matcher(req.getRequestURL()).matches())) {
676:
677: // It is Axis request? If it is, initialize the AxisServlet!
678: if (axisServlet == null) {
679: logChannel.write(Logger.INFO,
680: "Initializing the Axis servlet "
681: + application.getName());
682: axisServlet = new AxisServlet();
683: axisServlet.init(getServletConfig());
684: }
685: try {
686: // Service the request
687: logChannel.write(Logger.INFO,
688: "Servicing the Axis request "
689: + application.getName());
690: axisServlet.service(req, resp);
691: } catch (Exception excp) {
692: //Do nothing! Proceed and try to service it Enhydra way
693: presentationManager.Run(
694: new ServletHttpPresentationRequest(req),
695: new ServletHttpPresentationResponse(resp,
696: domStatsLogWriter));
697: }
698: } else
699:
700: presentationManager.Run(
701: new ServletHttpPresentationRequest(req),
702: new ServletHttpPresentationResponse(resp,
703: domStatsLogWriter));
704: } catch (HttpPresentationException except) {
705: throwServletException(
706: "An unhandled error occurred while servicing an application request",
707: except);
708: } finally {
709: // Be sure the thread is unregistered no matter what.
710: Enhydra.unRegister();
711: }
712: }
713:
714: /**
715: * This method is public for use by the ServletManager
716: * to run the application startup() method after init().
717: * @exception ServletException
718: * If any error occurs on startup.
719: */
720: public void ensureAppIsRunning() throws ServletException {
721: checkAutoReload();
722: // If an application is associated,
723: // then make sure it's in the run state.
724: if ((application != null)
725: && (application.getState() != Application.RUNNING)) {
726: try {
727: changeToRunningState();
728: context.setAttribute(EnhydraServer.SESSION_MANAGER_KEY,
729: application.getSessionManager());
730: } catch (Throwable except) {
731: throwServletException(
732: "Unable to change application to running state",
733: except);
734: }
735: }
736: }
737:
738: /**
739: * Return information specific to this servlet.
740: */
741: public String getServletInfo() {
742: return com.lutris.Enhydra.getEnhydraLongName() + "/"
743: + com.lutris.Enhydra.getEnhydraVersion(); // FIXME: return some valid info
744: }
745:
746: /**
747: * This method instantiates the HttpPresentationRequest
748: * object that the presentation manager expects when
749: * servicing a request.
750: *
751: * @param req
752: * The servlet request object.
753: * @param resp
754: * The servlet response object.
755: * @return The HttpPresentationRequest object used
756: * by the presentation manager.
757: */
758: protected HttpPresentationRequest getHttpPresentationRequest(
759: HttpServletRequest req, HttpServletResponse resp) {
760: return new ServletHttpPresentationRequest(req);
761: }
762:
763: /**
764: * This method instantiates the HttpPresentationResponse
765: * object that the presentation manager expects when
766: * servicing a request.
767: *
768: * @param req
769: * The servlet request object.
770: * @param resp
771: * The servlet response object.
772: * @return The HttpPresentationResponse object used
773: * by the presentation manager.
774: */
775: protected HttpPresentationResponse getHttpPresentationResponse(
776: HttpServletRequest req, HttpServletResponse resp) {
777: return new ServletHttpPresentationResponse(resp,
778: domStatsLogWriter);
779: }
780:
781: /**
782: * Returns the application being run by this instance of
783: * the servlet.
784: */
785: public Application getApplication() {
786: return application;
787: }
788:
789: /**
790: * Returns the application configuration init parameter.
791: */
792: protected Config getAppConfigInitParam() {
793: return appConfig;
794: }
795:
796: /**
797: * Returns the application configuration init parameter name.
798: */
799: protected String getAppConfigInitParamName() {
800: return APP_CONFIG_INIT_PARAM_NAME;
801: }
802:
803: /**
804: * Handle a service request. Use the first filter to wrap the request,
805: * response, and servlet (this) objects. This will return new objects.
806: * Then use the second filter to wrap those, again returning new
807: * objects. Then use the fourth filter to wrap those etc....
808: * Then finally call <CODE>service()</CODE> on the outermost results.
809: *
810: * @param request
811: * The original servlet request object.
812: * @param response
813: * The original servlet response object.
814: */
815: public void service(HttpServletRequest request,
816: HttpServletResponse response) throws ServletException,
817: IOException {
818: /*
819: * This hook was added to support the debugger. Almost all
820: * applications will not use this.
821: */
822: if (presentationManager.servletRequestPreprocessor(this ,
823: context, request, response)) {
824: // The request has been completely processed. Nothing left to do.
825: return;
826: }
827:
828: serviceDirect(request, response);
829: }
830:
831: /**
832: * Looks up the session object (if any) that would be used to
833: * process the request. This is will not used normally, because the
834: * session is give to the application's preprocessor method and the
835: * presentation objects. Also, it is not normal to have a raw
836: * ServletRequest. The debugger uses this to examine the session data
837: * before and after each request. Consider this an internal use only
838: * method.
839: *
840: * @param request
841: * The (raw) request that would be sent in to this application.
842: * @return
843: * The session object that would be associated with the request.
844: * Returns null if none is found; a new session is not created.
845: */
846: public Session getSession(ServletRequest request) {
847: if (application == null) {
848: return null;
849: }
850: Enhydra.register(application);
851: Session s = null;
852:
853: try {
854: s = presentationManager.getSession(request);
855: } finally {
856: Enhydra.unRegister();
857: }
858: return s;
859: }
860:
861: /**
862: * Check if the application should be automatically reloaded and, if so,
863: * call <CODE>initApplication</CODE> with a new instance of the class
864: * loader. The application should be reloaded if the
865: * <CODE>autoReload</CODE> flag is <CODE>true</CODE> and any class has
866: * changed in the application's classpath.
867: *
868: * @exception ServletException if a servlet exception has occurred.
869: */
870: private void checkAutoReload() throws ServletException {
871: if (autoReload) {
872: if (this .appClassLoader.shouldReload()) {
873: application.shutdown();
874:
875: ClassLoader secondaryLoader = Thread.currentThread()
876: .getContextClassLoader(); //vr
877: this .appClassLoader = new MultiClassLoader(
878: secondaryLoader.getParent(), secondaryLoader,
879: logChannel); //vr
880: appClassLoader.forceSecondaryLoader(true);
881: appClassLoader.enableAutoReloadForSecLoader(autoReload);
882:
883: initConfig(getServletConfig());
884: initApplication(getServletConfig());
885: }
886: }
887: }
888:
889: /**
890: * Destroys the servlet. Called by the servlet manager when the servlet
891: * is shutdown.
892: */
893: public synchronized void destroy() {
894: // for MemoryPersistence = false remove ClassLoader from HashTable
895: /* if (!isMemoryPersistence) {
896: MemoryPersistence.removeClassLoader(appName);
897: }
898: */
899: application.shutdown();
900: // Unregister from EnhydraServer
901: EnhydraServer es = EnhydraServer.getInstance();
902: es.unRegister(this );
903:
904: // Clearing Object Attributes
905: standardLogger = null;
906: logChannel = null;
907: appClassLoader.clearClassPath();
908: appClassLoader = null;
909: presentationManager = null;
910: }
911:
912: }
|