KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > appserver > server > httpPresentation > HttpPresentationManager


1
2 /*
3  * Enhydra Java Application Server Project
4  *
5  * The contents of this file are subject to the Enhydra Public License
6  * Version 1.1 (the "License"); you may not use this file except in
7  * compliance with the License. You may obtain a copy of the License on
8  * the Enhydra web site ( http://www.enhydra.org/ ).
9  *
10  * Software distributed under the License is distributed on an "AS IS"
11  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
12  * the License for the specific terms governing rights and limitations
13  * under the License.
14  *
15  * The Initial Developer of the Enhydra Application Server is Lutris
16  * Technologies, Inc. The Enhydra Application Server and portions created
17  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
18  * All Rights Reserved.
19  *
20  * Contributor(s):
21  *
22  * $Id: HttpPresentationManager.java,v 1.3 2005/03/24 10:51:16 slobodan Exp $
23  */

24
25
26
27
28
29 package com.lutris.appserver.server.httpPresentation;
30
31 //import com.lutris.http.*;
32
import java.io.IOException JavaDoc;
33 import java.io.InputStream JavaDoc;
34 import java.util.Enumeration JavaDoc;
35
36 import javax.servlet.Servlet JavaDoc;
37 import javax.servlet.ServletContext JavaDoc;
38 import javax.servlet.ServletException JavaDoc;
39 import javax.servlet.ServletRequest JavaDoc;
40 import javax.servlet.http.Cookie JavaDoc;
41 import javax.servlet.http.HttpServletRequest JavaDoc;
42 import javax.servlet.http.HttpServletResponse JavaDoc;
43
44 import com.lutris.appserver.server.Application;
45 import com.lutris.appserver.server.ApplicationException;
46 import com.lutris.appserver.server.StandardAppUtil;
47 import com.lutris.appserver.server.session.Session;
48 import com.lutris.appserver.server.user.User;
49 import com.lutris.appserver.server.user.UserImpl;
50 import com.lutris.logging.LogChannel;
51 import com.lutris.logging.Logger;
52 import com.lutris.util.ConfigException;
53 /**
54  * Presentation manager class. Handles loading and execution of
55  * presentation objects.
56  *
57  * @version $Revision: 1.3 $
58  * @author Mark Diekhans
59  */

60 public class HttpPresentationManager {
61
62     /**
63      * Maximum number of times to go through page redirect/error
64      * handling loops.
65      */

66     private final int maxRedirectErrorLoops = 25;
67
68     /**
69      * Presentation and resource loader/cache.
70      */

71     private PresentationLoader loader;
72
73     /**
74      * Application that this presentation manager is associate with.
75      */

76     private Application application;
77
78     /**
79      * Applications log channel. No logging is done if null.
80      */

81     private LogChannel logChannel;
82
83     /**
84      * Is DEBUG logging enabled?
85      */

86     private boolean debugLoggingEnabled;
87
88     /**
89      * Request id incremented on each request.
90      */

91     private long nextRequestId = 0;
92
93
94     /**
95      * The Servlet we are running in. Will be an instance of
96      * <CODE>
97      * com.lutris.appserver.server.httpPresenation.HttpPresentationServlet
98      * </CODE>.
99      */

100     private Servlet JavaDoc servlet;
101
102     /**
103      * The ServletContext that was used to initialize the Servlet we are
104      * running in. This is needed to, for example, get the other servlets
105      * that are our peers in the server.
106      */

107     private ServletContext JavaDoc servletContext;
108
109     private long limitMilis = -11;
110     
111     private boolean initSessionUser = false;
112
113     private String JavaDoc PARAM_LIMIT_MILIS = "PresentationManager.RequestExecutionLimit";
114     
115     private String JavaDoc PARAM_SESSION_USER = "PresentationManager.InitSessionUser";
116
117
118     /**
119      * Construct a HTTP presentation manager.
120      *
121      * @param appPresentationPrefix Prefix added to the file name portion
122      * of the URL. The resulting names is used to search for classes and
123      * files on the class path.
124      * @param presApplication Application object that this presentation is
125      * associated with.
126      * @param applicationClassLoader
127      * the class loader to use for the application.
128      * @param cacheClasses Enables or disables caching of presentation object
129      * classes in memory.
130      * @param cacheFiles Enables or disables caching of files (html, gif, etc)
131      * that are servered as part of the application.
132      */

133     public HttpPresentationManager(String JavaDoc appPresentationPrefix,
134                                    Application presApplication,
135                                    ClassLoader JavaDoc applicationClassLoader,
136                                    boolean cacheClasses,
137                                    boolean cacheFiles)
138             throws HttpPresentationException {
139
140         application = presApplication;
141     logChannel = application.getLogChannel();
142     if (logChannel != null) {
143         debugLoggingEnabled = logChannel.isEnabled(Logger.DEBUG);
144         }
145         loader = new PresentationLoader(appPresentationPrefix,
146                                         applicationClassLoader,
147                                         cacheClasses,
148                                         cacheFiles,
149                                         logChannel);
150     }
151
152
153
154
155
156     /**
157      * The method is invoked to fulfill an HTTP request. This function is
158      * thread-safe and does not need synchronization. It is expected that the
159      * protocol layer invoking this function provide multi-threading to handle
160      * requests.
161      *
162      * The alogrithm for handling a request is:
163      * <PRE>
164      * presObj = requestUrl
165      * while request-not-handled {
166      * if not error-handler-redirect
167      * invoke-application-request-preprocessor presObj
168      * load-presentation-object-class
169      * invoke-presentation-object
170      * if not error-handler-redirect
171      * invoke-application-request-postprocessor presObj
172      * if (client-side-page-redirect-exception) {
173      * send-redirect-to-client
174      * break
175      * }
176      * if (server-side-page-redirect-exception) {
177      * (Not implemented yet)
178      * set-request-to-redirectUrl
179      * presObj = redirectUrl
180      * continue
181      * }
182      * if (401-unauthorized-exception) {
183      * set-header-and-response-code
184      * break
185      * }
186      * if (other-exception) {
187      * presObj = find-error-handler
188      * }
189      * }
190      * </PRE>
191      *
192      * @param request Request object for the protocol invoking the manager.
193      * @param response Response object for the protocol invoking the manager.
194      * @exception HttpPresentationException All exceptions will be encapsulated
195      * in an exception of this class.
196      */

197     public void Run(HttpPresentationRequest request,
198                     HttpPresentationResponse response)
199         throws HttpPresentationException, IOException JavaDoc {
200
201         long requestId;
202
203         synchronized (this) {
204             requestId = nextRequestId++;
205         }
206
207
208         //FIXME: Error handle has gotten very complex and needs to be
209
// revisited (again).
210
HttpPresentationComms comms =
211         new HttpPresentationComms (request, response, application);
212
213         /*
214          * Loop calling the presentation and then PageRedirect or ErrorHandlers
215          * until the request is handled. This is a state machine that is either
216          * loading request page or an error handler. Server side redirect can
217          * cause a state transition back to the request page state.
218          * The application can also handle the request.
219          */

220
221         /*
222          * N.B. getPresentationObjectPathInfo may return null on an invalid
223          * URL or empty, at least with servlet runner and using just the
224          * name of the application. Handled by runPresObj.
225          */

226     String JavaDoc presObjPath = request.getPresentationObjectRelativePath();
227
228         if (presObjPath == null) {
229             // If presObj is null, it could be an invalid URL that just
230
// references the application. See what happens.
231
presObjPath = "/";
232         }
233
234         String JavaDoc requestedPrseObjPath = presObjPath;
235         int loopCount = 0;
236
237         // When this variable is not-null, an exception is being handled.
238
Throwable JavaDoc exception = null;
239
240         presLoop:
241         while (true) {
242             logPresObjRun(comms, presObjPath, exception,
243               requestedPrseObjPath, requestId);
244             loopCount++;
245             try {
246                 if (exception == null) {
247                     if (runRequestPreprocessor(comms, requestId,
248                                                presObjPath)) {
249                         break presLoop; // Finished!
250
}
251                 }
252                 runPresentationObj(comms, presObjPath, exception, application);
253                 break presLoop; // Finished!`
254
} catch (PageRedirectException except) {
255         presObjPath = PageRedirect.handler(comms, presObjPath, except, logChannel, requestId);
256                 if (presObjPath == null) {
257                     break presLoop; // Finished!
258
}
259                 exception = null; // Server redirected, No longer handling an exception.
260
} catch (PageUnauthorizedException except) {
261                 ExceptionHandler.sendUnauthorizedPage(comms,
262                               logChannel,
263                                                       requestId,
264                                                       application,
265                                                       presObjPath,
266                                                       except);
267
268                 break presLoop; // Finished!
269
} catch (Throwable JavaDoc except) {
270         logChannel.write(Logger.DEBUG, "RID:" + requestId
271                                  + ": exception running presentation"
272                                  + presObjPath,
273                                  except);
274                 // Don't execute error handler on client disconnect.
275
if (HttpPresentationIOException.isClientIOException(except)) {
276                     // We don't except this to actually display anything, just
277
// log.
278
ExceptionHandler.logDisplayException(comms, logChannel, requestId,
279                                                          application, except, request.getPathInfo());
280                     // We always run the post processor
281
try {
282                         runRequestPostProcessor(comms, requestId, presObjPath);
283                     } catch (Throwable JavaDoc e) {
284                         // FIX - OK? - should log details of error.
285
logChannel.write(Logger.DEBUG, "RID:" + requestId
286                                          + ": Request Post Proecessor exception: "
287                                          + presObjPath + ": " + e);
288                     }
289                     return; // Finished, but don't break to second flush.
290
}
291
292                 // Look for the next error handler.
293
String JavaDoc exceptpresObjPath = presObjPath;
294                 presObjPath =
295             ExceptionHandler.getErrorHandler(presObjPath,
296                              requestedPrseObjPath,
297                              (exception != null),
298                              loader,
299                              logChannel,
300                              requestId);
301                 if (presObjPath == null) {
302                     // Top of tree, no more places to check.
303
ExceptionHandler.processUnhandledException(comms,
304                                                                logChannel,
305                                                                requestId,
306                                                                application,
307                                                                requestedPrseObjPath,
308                                                                except);
309                     break presLoop; // Finished!
310
}
311                 exception = except; // Now handling this exception.
312
}
313             if (loopCount > maxRedirectErrorLoops) {
314                 ExceptionHandler.maxRedirectsReached(comms,
315                                                      logChannel,
316                                                      requestId,
317                                                      maxRedirectErrorLoops,
318                                                      application,
319                                                      presObjPath);
320                 break presLoop; // Finished!
321
}
322         }
323
324
325         try {
326             response.flush();
327             runRequestPostProcessor(comms, requestId, presObjPath);
328         } catch (Throwable JavaDoc except) {
329             // We don't except this to actually display anything, just
330
// log.
331
ExceptionHandler.logDisplayException(comms, logChannel, requestId, application, except,
332                                                  request.getPathInfo());
333         }
334     }
335
336
337     /**
338      * Log the execution of a presentation object.
339      */

340     private void logPresObjRun(HttpPresentationComms comms,
341                                String JavaDoc presObjPath,
342                                Throwable JavaDoc exception,
343                                String JavaDoc requestedPrseObjPath,
344                                long requestId) throws HttpPresentationException {
345         if (debugLoggingEnabled) {
346             if (exception != null) {
347         logChannel.write(Logger.DEBUG, "RID:" + requestId
348                                  + ": presentation run exception handler: "
349                                  + presObjPath + " for:\n"
350                                  + " " + exception.getClass().getName()
351                                  + ": " + exception.getMessage()
352                                  + "\nwhile accessing: "
353                                  + requestedPrseObjPath);
354             } else if (comms.request.getMethod().equals("HEAD")) {
355         logChannel.write(Logger.DEBUG, "RID:" + requestId
356                                  + ": presentation head : " + presObjPath);
357             } else {
358         logChannel.write(Logger.DEBUG, "RID:" + requestId
359                                  + ": presentation run: " + presObjPath);
360             }
361         }
362     }
363
364
365     /**
366      * This is an internal use only hook, see the description in
367      * Application.java. The call is passed through to the application.
368      */

369     public boolean servletRequestPreprocessor(Servlet JavaDoc me,
370                                    ServletContext JavaDoc context,
371                                    HttpServletRequest JavaDoc request,
372                                    HttpServletResponse JavaDoc response)
373                    throws ServletException JavaDoc, IOException JavaDoc {
374         if (application != null)
375             return application.servletRequestPreprocessor(
376                                     me, context, request, response);
377         else
378             return false;
379     }
380
381
382
383     /**
384      * Run the request preprocessor.
385      */

386     private boolean runRequestPreprocessor(HttpPresentationComms comms,
387                                            long requestId,
388                                            String JavaDoc presObjPath) throws Exception JavaDoc {
389         //DACHA patch: Do not allow "*.class" resource to be accesible !
390
String JavaDoc checkPath = presObjPath.trim();
391         if(checkPath.length() > 6){ // ".class" length = 6
392
checkPath = checkPath.substring(checkPath.length()-6,checkPath.length());
393             if(checkPath.equalsIgnoreCase(".class")){
394                 // Send ERROR message
395
comms.response.sendError(501, "Enhydra application: Access forbidden, *.class resource not alowed!");
396                 return true;
397             }
398         }
399         //END DACHA
400
if (application.requestPreprocessor(comms)) {
401             if (debugLoggingEnabled) {
402         logChannel.write(Logger.DEBUG, "RID:" + requestId
403                                  + ": application.requestPreprocessor finished request: "
404                                  + presObjPath);
405             }
406             return true;
407         }
408         return false;
409     }
410
411     /**
412      * Run the request post-processor.
413      */

414     private void runRequestPostProcessor(HttpPresentationComms comms,
415                                          long requestId,
416                                          String JavaDoc presObjPath)
417         throws Exception JavaDoc {
418         application.requestPostProcessor(comms);
419     }
420
421     /**
422      * Load and run a presentation object (including error handlers).
423      * A return without exception indicates that the presentation has been
424      * processed. Will rethrow an exception if one is being handled and
425      * a handler is not found.
426      *
427      * @param comms
428      * comms object.
429      * @param urlPath
430      * the path of the url.
431      * @param exception
432      * if not null, it indicates that an exception or error is being handled.
433      * @param application
434      * the application
435      */

436     private void runPresentationObj(HttpPresentationComms comms,
437                                     String JavaDoc urlPath,
438                                     Throwable JavaDoc exception,
439                                     Application application) throws Throwable JavaDoc {
440
441         HttpPresentation presObj;
442         long milis = 0;
443         comms.exception = exception;
444         
445         try {
446             initSessionUser = application.getConfig().getBoolean(PARAM_SESSION_USER, false);
447         } catch (ConfigException ex) {
448             logChannel.write(Logger.WARNING,
449                                  "Couldn't get "+ PARAM_SESSION_USER +" parameter",ex);
450         }
451         if (limitMilis < -9) {
452             try {
453                 limitMilis = application.getConfig().getInt(PARAM_LIMIT_MILIS, -1);
454             } catch (ConfigException e) {
455                 logChannel.write(Logger.WARNING,
456                                  "Couldn't get "+ PARAM_LIMIT_MILIS +" parameter",
457                                  e);
458             } finally {
459                 if (limitMilis < 0) limitMilis = -1;
460             }
461         }
462
463         // Get the presentation object. If a class is not found and we
464
// where handling an error, rethrow that error.
465
try {
466             presObj = loader.loadPresentation(urlPath);
467         } catch (ClassNotFoundException JavaDoc noClass) {
468             if (exception != null) {
469                 throw exception; // ErrorHandler was not found.
470
} else {
471                 throw noClass;
472             }
473         }
474         
475         if (0 <= limitMilis) {
476             milis = System.currentTimeMillis();
477         }
478         
479         presObj.run(comms);
480         
481         if (initSessionUser){
482             //Adding Request Session User to request session
483
try{
484                 User userObj = comms.session.getUser();
485             String JavaDoc userName = null;
486                 if ( null==userObj ){
487                     userName = comms.request.getRemoteUser();
488                     if(null==userName){
489                         userName = comms.request.getHeader(":remoteuser");
490                     }
491                 if (null == userName){
492                         userName = "ANONYMOUS";
493                 }
494                 userObj = new UserImpl(userName);
495                 comms.session.setUser(userObj);
496                 }
497         }catch(NullPointerException JavaDoc ex){}
498         }
499         
500         if (0 <= limitMilis) {
501             
502             milis = System.currentTimeMillis() - milis;
503             //String user = comms.request.getHeader("REMOTE_USER");
504

505             String JavaDoc user = comms.request.getRemoteUser();
506             if(null==user)
507                 user = comms.request.getHeader(":remoteuser");
508             
509             Cookie JavaDoc[] c = comms.request.getCookies();
510             if (null == user)
511                 user = "ANONYMOUS";
512             user += "(";
513             if (c != null) {
514                 for (int n = 0; n < c.length; ++n) {
515                     user += c[n].getName()+"="+c[n].getValue();
516                     if (c.length - 1 != n) {
517                         user += ", ";
518                     }
519                 }
520             }
521             user += ")";
522             if (0 == limitMilis || milis > limitMilis) {
523                 String JavaDoc params ="";
524                 for (Enumeration JavaDoc e = comms.request.getParameterNames();
525                      e.hasMoreElements();) {
526                     String JavaDoc pName =(String JavaDoc)e.nextElement();
527                     params +="&"+ pName +"="+ comms.request.getParameter(pName);
528                 }
529                 logChannel.write(Logger.WARNING, comms.request.getMethod()
530                                      +" "
531                                      + urlPath
532                                      + params
533                                      +" requested by "
534                                      + user
535                                      +" from "
536                                      + comms.request.getRemoteHost()
537                                      +"["
538                                      + comms.request.getRemoteAddr()
539                                      +"] took "
540                                      + milis
541                                      +"ms to run");
542             }
543             
544         }
545     }
546
547
548     /**
549      * Determine if the PO cache is enabled.
550      *
551      * @return <code>true</code> is the cache is enabled.
552      */

553     public boolean isPOCacheEnabled() {
554         return loader.isPOCacheEnabled();
555     }
556
557     /**
558      * Return the number of entries in the PO cache.
559      *
560      * @return the number of entries in the cache or 0 is disabled.
561      */

562     public int sizeofPOCache() {
563         return loader.sizeofPOCache();
564     }
565
566     /**
567      * Determine if the file resource class cache is enabled.
568      *
569      * @return <code>true</code> is the cache is enabled.
570      */

571     public boolean isResourceCacheEnabled() {
572         return loader.isResourceCacheEnabled();
573     }
574
575     /**
576      * Return the number of entries in the resource cache.
577      *
578      * @return the number of entries in the cache or 0 is disabled.
579      */

580     public int sizeofResourceCache() {
581         return loader.sizeofResourceCache();
582     }
583
584     /**
585      * Get a file associated with the application. The file is located
586      * in the same manner as presentation objects are located. That is,
587      * prepending the <CODE>presentationPrefix</CODE> to the specified
588      * path and searching the class path for a directory or JAR containing
589      * the file.
590      *
591      * @param appfileName The file name relative to the
592      * to the application's <CODE>presentationPrefix</CODE>.
593      * @return An input stream associated with the file.
594      * @exception IOException If the file can not be found or opened.
595      */

596     public InputStream JavaDoc getAppFileAsStream(String JavaDoc appFileName)
597             throws IOException JavaDoc, HttpPresentationException {
598         return loader.getAppFileAsStream(appFileName);
599     }
600
601     /**
602      * Determine if a request URL references a presentation object.
603      *
604      * @param request The request for the presentation.
605      * @return true if the URL has a presentation object MIME type.
606      */

607     public boolean isPresentationRequest(HttpPresentationRequest request)
608             throws HttpPresentationException {
609         return loader.isPresentationRequest(request);
610     }
611
612
613     /**
614      * Looks up the session object (if any) that would be used to
615      * process the request. This is will not used normally, because the
616      * session is give to the application's preprocessor method and the
617      * presentation objects. Also, it is not normal to have a raw
618      * ServletRequest. The debugger uses this to examine the session data
619      * before and after each request. Consider this an internal use onlyu
620      * method.
621      *
622      * @param request
623      * The (raw) request that would be sent in to this application.
624      * @return
625      * The session object that would be associated with the request.
626      * Returns null if none is found; a new session is not created.
627      */

628     public Session getSession(ServletRequest JavaDoc request) {
629         if (application == null)
630             return null;
631         Session s = null;
632         try {
633             s = StandardAppUtil.getRequestSession(request, application);
634         } catch (ApplicationException e) {
635         }
636         return s;
637     }
638
639
640
641     /*
642      * The next 3 functions were added to that applications can
643      * get their neghbor servlets. In the beginning we were prepared
644      * to leave servlets behind and move on to a new api if they
645      * didn't catch on. They caught on. So in the future we could
646      * simplify the archetecture some by not hiding the servlets
647      * from the applications with an abstraction layer.
648      */

649
650
651     /**
652      * Notify the presentation manager which servlet we are running in.
653      * This will be done immediatly after this object is created, if
654      * this ojbject is created by an HttpPresentationServlet (currently
655      * the only creator). In the future, if this class is used in non-servlet
656      * situations, this need not be called.
657      *
658      * @param servlet
659      * The servlet we are running in.
660      * @param servletContext
661      * The ServletContext used to initialize the servlet we are running in.
662      */

663     public void setServletAndContext(Servlet JavaDoc servlet,
664                                      ServletContext JavaDoc servletContext) {
665         this.servlet = servlet;
666         this.servletContext = servletContext;
667     }
668
669
670     /**
671      * Get the servlet we are running in. Currently this class is only
672      * used in a servlet (HttpPresentationServlet), so this value will
673      * always be set. But in the future this class may be used in other
674      * non-servlet enviornments, in which case this method will return
675      * null.
676      *
677      * @return
678      * The servlet we are running in, if any.
679      */

680     public Servlet JavaDoc getServlet() {
681         return this.servlet;
682     }
683
684
685     /**
686      * Get the servlet context used to initialize the servlet we are running
687      * in. Currently this class is only used in a servlet
688      * (HttpPresentationServlet), so this value will always be set. But in
689      * the future this class may be used in other non-servlet enviornments,
690      * in which case this method will return null.
691      *
692      * @return
693      * The servlet context used to initialize the servlet we are
694      * running in, if any.
695      */

696     public ServletContext JavaDoc getServletContext() {
697         return this.servletContext;
698     }
699
700     /**
701      * Flush the presentation object and resource caches.
702      */

703     public void flushCache() {
704         loader.flushCache();
705     }
706
707     /** Add a new mime type to extension mapping. */
708     public void addMimeType(String JavaDoc mimeType, String JavaDoc extension) {
709         loader.addMimeType(mimeType, extension);
710     }
711
712     /**
713      * Get the application class loader.
714      *
715      * @return The application class loader.
716      */

717     public ClassLoader JavaDoc getAppClassLoader() {
718         return loader.getAppClassLoader();
719     }
720 }
721
Popular Tags