package com.foundation.web; import java.io.InputStream; import java.net.URI; import com.common.debug.Debug; import com.common.io.StreamSupport; import com.common.thread.IRunnable; import com.common.util.IHashMap; import com.common.util.LiteHashMap; import com.foundation.web.WebApplication.PathSubstitution; import com.foundation.web.interfaces.IConnectionContext; import com.foundation.web.interfaces.IRequest; import com.foundation.web.interfaces.IResponse; import com.foundation.web.interfaces.ISession; import com.foundation.web.interfaces.IWebApplication; import com.foundation.web.SessionData; import com.foundation.web.SecureSessionData; /** * Allows the application to specify special handling for access to static resources. */ public abstract class ResourceRequestHandler { /** The framework.js source. */ private String frameworkJavascript; /** * ResourceRequestHandler constructor. */ public ResourceRequestHandler() { try { InputStream in = getClass().getClassLoader().getResourceAsStream("framework.js"); try { frameworkJavascript = StreamSupport.readText(in, "UTF8"); }//try// finally { try {in.close();} catch(Throwable e) {} }//finally// }//try// catch(Throwable e) { Debug.log("Failed to read the framework.js file from the framework jar.", e); }//catch// }//ResourceRequestHandler()// /** * Allows the handler to substitute one path for another. * @param path The path that was provided by the client. * @param request The request object. * @param response The response object. * @return The path to be used when locating the resource. May be null if no furthor processing of the request should be performed. */ protected String performPathSubstitution(String path, IRequest request, IResponse response) { return path; }//performPathSubstitution()// /** * Gets a resource from the file system. The implementor can validate that the user has the rights to access the resource and can ensure that there is an SSL connection if needed. *

A simple implementation will simply call: response.setContentResource(requestPath, request.getCacheDate());, a more complex implementation will test for an SSL connection for secure resources, and will ensure the user is logged in and has rights to access the resource for rights oriented resources.

*

WARNING: The caller of this method synchronizes on the internal web server session for the client. Because of this, all code handling the request that requires session access *MUST* ensure this thread exits the method AFTER all worker threads that might be created or used. This is also important because the Response object should never be modified outside the scope of this method call.

* @param requestPath The path to the requested resource. This is the request's URI already mapped to an actual resource (same as calling request.getUri()). * @param request The request object. * @param response The response object. * @param sessionData The non-secure session data or null if there is none for the application. * @param secureSessionData The secure session data, or null if the request was made over an insecure connection or no secure session data exists. * @param isSecure Whether the request was made over a secure connection and provided the correct secure id. * @param connectionContext The context object for the connection (socket) between the client (web browser) and server (web server). */ public void processRequest(final IRequest request, final IResponse response, final SessionData sessionData, final SecureSessionData secureSessionData, final boolean isSecure, final IConnectionContext connectionContext) { int requestType = request.getRequestType(); boolean isGetPostOrHead = requestType == IRequest.TYPE_GET || requestType == IRequest.TYPE_POST || requestType == IRequest.TYPE_HEAD; boolean isWebdav = requestType == IRequest.TYPE_WEBDAV_COPY || requestType == IRequest.TYPE_WEBDAV_LOCK || requestType == IRequest.TYPE_WEBDAV_MKCOL || requestType == IRequest.TYPE_WEBDAV_MOVE || requestType == IRequest.TYPE_WEBDAV_PROPFIND || requestType == IRequest.TYPE_WEBDAV_PROPPATCH || requestType == IRequest.TYPE_WEBDAV_UNLOCK; String path = request.getUri(); IWebApplication webApplication = request.getWebApplication(); while(path != null) { path = performPathSubstitution(path, request, response); if(path != null) { boolean isFrameworkController = path.equals("/Framework") || path.equals("/Framework.java") || path.equals("/FrameworkController") || path.equals("/FrameworkController.java"); boolean isFrameworkJavascript = path.equals("/framework.js"); //Verification of rights to access the resource are handled by the IResourceRequestHandler setup by the application. If an IResourceRequestHandler is provided then it can perform any validation it sees fit prior to allowing access to the requested resource.// if((path.endsWith(".java") || isFrameworkController) && isGetPostOrHead) { try { final IWebRequestHandler handler; Object instance = null; if(isFrameworkController) { instance = new FrameworkController(); }//if// else { String clsName = path.replace('/', '.').substring(0, path.length() - 5); Class cls = null; if(clsName.startsWith(".")) { clsName = clsName.substring(1); }//if// cls = Class.forName(webApplication.getDefaultPackageName() + clsName); instance = cls.newInstance(); }//else// if(instance instanceof IWebRequestHandler) { handler = (IWebRequestHandler) instance; }//if// else { Debug.log(new RuntimeException("Unexpected class called by the client: " + instance.getClass().getName())); handler = null; }//else// if(handler != null) { final String requestPath = path; IRunnable runnable = new IRunnable() { public Object run() { processRequest(handler, requestPath, request, response, sessionData, secureSessionData, isSecure, connectionContext); return null; }//run()// }; //Ensure the request is processed on the correct thread.// if(sessionData != null && sessionData.getRequestHandler() != null) { //Note: This is a synchronous call, so no need to increment/decrement the activity counter on the web application container.// sessionData.getRequestHandler().execute(runnable, true); }//if// else { runnable.run(); }//else// }//if// }//try// catch(Throwable e) { Debug.log(e); response.setError(response.ERROR_TYPE_RESOURCE_NOT_FOUND); }//catch// }//if// else if(isGetPostOrHead) { if(isFrameworkJavascript) { JavascriptContent content = new JavascriptContent(frameworkJavascript); //Attempt to tell the client not to cache.// content.setCacheLength(content.CACHE_LENGTH_NEVER_CACHE); content.setExpiresDirective(null); response.setContent(content); }//if// else { processRequest(path, request, response, sessionData, secureSessionData, isSecure, connectionContext); }//else// }//else if// else if(isWebdav) { processWebDavRequest(path, request, response, sessionData, secureSessionData, isSecure, connectionContext); }//else if// else { //The request type is PUT or OPTIONS.// //TODO: Provide a way for applications to provide support for these requests. response.setError(IResponse.ERROR_TYPE_INVALID_ACCESS); }//else// if(response.getRedirectUri() != null) { try { path = new URI(path).resolve(new URI(response.getRedirectUri())).getPath(); }//try// catch(Throwable e) { Debug.log(e); //TODO: Use a better error - internal error. response.setError(IResponse.ERROR_TYPE_RESOURCE_NOT_FOUND); path = null; }//catch// response.setRedirectUri(null); }//if// else { path = null; }//else// }//if// }//while// }//processRequest()// /** * Gets a resource from the file system. The implementor can validate that the user has the rights to access the resource and can ensure that there is an SSL connection if needed. *

A simple implementation will simply call: response.setContentResource(requestPath, request.getCacheDate());, a more complex implementation will test for an SSL connection for secure resources, and will ensure the user is logged in and has rights to access the resource for rights oriented resources.

* @param requestPath The path to the requested resource. This is the request's URI already mapped to an actual resource (same as calling request.getUri()). * @param request The request object. * @param response The response object. * @param sessionData The non-secure session data. * @param secureSessionData The secure session data, or null if the request was made over an insecure connection or no secure session data exists. * @param isSecure Whether the request was made over a secure connection and provided the correct secure id. * @param connectionContext The context object for the connection (socket) between the client (web browser) and server (web server). */ public abstract void processRequest(String requestPath, IRequest request, IResponse response, SessionData sessionData, SecureSessionData secureSessionData, boolean isSecure, IConnectionContext connectionContext); /** * Handles a WebDav request. * @param requestPath The path to the requested resource. This is the request's URI already mapped to an actual resource (same as calling request.getUri()). * @param request The request object. * @param response The response object. * @param sessionData The non-secure session data. * @param secureSessionData The secure session data, or null if the request was made over an insecure connection or no secure session data exists. * @param isSecure Whether the request was made over a secure connection and provided the correct secure id. * @param connectionContext The context object for the connection (socket) between the client (web browser) and server (web server). */ public void processWebDavRequest(String requestPath, IRequest request, IResponse response, SessionData sessionData, SecureSessionData secureSessionData, boolean isSecure, IConnectionContext connectionContext) { //Does nothing.// }//processWebDavRequest()// /** * Calls a handler to access the requested resource. The implementor can validate that the user has the rights to access the resource and can ensure that there is an SSL connection if needed. *

A simple implementation will simply call: response.setContentResource(requestPath, request.getCacheDate());, a more complex implementation will test for an SSL connection for secure resources, and will ensure the user is logged in and has rights to access the resource for rights oriented resources.

* @param handler The handler which will process the request. * @param requestPath The path to the requested resource. This is the path from the application's default package name to the handler class using slashes instead of dots as separators. * @param request The request object. * @param response The response object. * @param sessionData The non-secure session data. * @param secureSessionData The secure session data, or null if the request was made over an insecure connection or no secure session data exists. * @param isSecure Whether the request was made over a secure connection and provided the correct secure id. * @param connectionContext The context object for the connection (socket) between the client (web browser) and server (web server). */ public abstract void processRequest(IWebRequestHandler handler, String requestPath, IRequest request, IResponse response, SessionData sessionData, SecureSessionData secureSessionData, boolean isSecure, IConnectionContext connectionContext); /** * Gets the maximum size of message allowed by the application beyond the header (for upload only). * @param request The request whose header has been read, but whose body has not yet been read. * @param sessionData The sessionData for the session that the request exists within. This may be null only if the application does not assign a session, or a session for this connection has not yet been assigned. * @param secureSessionData The secure session data for the session. This may be null if a session has not or will not be assigned, or if the connection to the client is not secure. * @return The maximum message size in bytes, or -1 if the message may be infinately large. This should be restricted unless the user is trusted to prevent malicious users from hogging network and system resources. */ public abstract int getMaxMessageSize(IRequest request, SessionData sessionData, SecureSessionData secureSessionData); }//IResourceRequestHandler//