MapperEngine.java :  » Web-Server » simple » simple » http » load » Java Open Source

Java Open Source » Web Server » simple 
simple » simple » http » load » MapperEngine.java
/*
 * MapperEngine.java May 2003
 *
 * Copyright (C) 2003, Niall Gallagher <niallg@users.sf.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General 
 * Public License along with this library; if not, write to the 
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
 * Boston, MA  02111-1307  USA
 */
  
package simple.http.load;

import simple.http.serve.StatusReport;
import simple.http.serve.Component;
import simple.http.serve.ErrorReport;
import simple.http.serve.CacheContext;
import simple.http.serve.Context;
import simple.http.serve.Report;
import simple.http.serve.Resource;
import simple.http.Response;
import simple.http.Request;
import java.io.IOException;
import java.net.URL;
import java.io.File;

/**
 * The <code>MapperEngine</code> provides an implementation of the
 * <code>LoaderEngine</code> that does not require administration.
 * It enables <code>Service</code> objects to be loaded and then
 * executed without the need to use manual <code>load</code>
 * invocations. This ability means that byte codes are taken from
 * the class path once they have been referenced.
 * <p>
 * The <code>MapperEngine</code> makes use of a system wide 
 * <code>Mapper</code> to determine what references, that is, what
 * URI references, map to a <code>Service</code> implementation.
 * If a URI maps to a service then this will check to determine
 * if it has been previously loaded, if so then it will simply
 * return that instance. However if it has not been loaded then
 * it will load the object and return a reference to it.
 * <p>
 * The loaded <code>Service</code> objects have an instance name
 * that is used to reference a specific object, this name is taken
 * from the <code>Mapper.getName</code> method. The service name
 * can also be used to acquire the fully qualified class name of
 * the service, this is useful when a service needs to be loaded.
 * This capability allows multiple instances of the same type to 
 * be loaded and referenced individually using the service name.
 * 
 * @author Niall Gallagher
 *
 * @see simple.http.load.MapperFactory
 * @see simple.http.load.Mapper
 */
public class MapperEngine extends LoaderEngine {

   /**
    * This is the mapper used by this engine to resolve URIs.
    */
   protected Mapper mapper;

   /**
    * Contains the object that is passed to each service object.
    */
   protected Object data;
   
   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes. This constructor uses a context for
    * the current working directory as a <code>CacheContext</code>.
    */
   public MapperEngine() throws IOException{
      this(new CacheContext());
   }   
   
   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes.
    * <p>
    * This will load the class byte codes from the base path of
    * the supplied <code>Context</code> object. If the byte codes
    * cannot be found using the inherited or system class loader
    * the base path is checked, failing that this will return a
    * service object that can supply an HTTP 404 Not Found.
    *
    * @param context the <code>Context</code> for this instance
    */
   public MapperEngine(Context context) throws IOException {
      this(context, context.getBasePath());
   }
   
   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes.
    * <p>
    * This will load the class byte codes from the base path of
    * the supplied <code>Context</code> object. If the byte codes
    * cannot be found using the inherided or system class loader
    * the base path is checked, failing that this will return a
    * service object that can supply an HTTP 404 Not Found.
    * <p>
    * This enables an arbitrary object to be issused, which is
    * what is used by this <code>LoaderEngine</code> to load the
    * <code>Service</code> objects. Each instance that is loaded
    * is given this object via its <code>prepare</code> method.
    *
    * @param context the <code>Context</code> for this instance
    * @param data this is the data that is given to each service
    */   
   public MapperEngine(Context context, Object data) throws IOException {
      this(context, context.getBasePath(), data);      
   }

   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes.
    * <p>
    * This will load the class byte codes from the supplied path,
    * if they cannot be found within the inherited or system class
    * path. Providing a path other than the <code>Context</code>
    * base path can be useful in seperating services from content.
    * If a service cannot be found an HTTP 404 Not Found is used.
    *
    * @param context the <code>Context</code> for this instance
    * @param path this is added to the classpath of the engine
    */ 
   public MapperEngine(Context context, String path) throws IOException{
      this(context, path, null);
   }

   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes.
    * <p>
    * This will load the class byte codes from the supplied path,
    * if they cannot be found within the inherited or system class
    * path. Providing a path other than the <code>Context</code>
    * base path can be useful in seperating services from content.
    * If a service cannot be found an HTTP 404 Not Found is used.
    * <p>
    * This enables an arbitrary object to be issused, which is
    * what is used by this <code>LoaderEngine</code> to load the
    * <code>Service</code> objects. Each instance that is loaded
    * is given this object via its <code>prepare</code> method.
    *
    * @param context the <code>Context</code> for this instance
    * @param path this is added to the classpath of the engine
    * @param data this is the data that is given to each service
    */ 
   public MapperEngine(Context context, String path, Object data) throws IOException{
      this(context, new File(path), data);
   }

   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes.
    * <p>
    * This will load the class byte codes from the supplied path,
    * if they cannot be found within the inherited or system class
    * path. Providing a path other than the <code>Context</code>
    * base path can be useful in seperating services from content.
    * If a service cannot be found an HTTP 404 Not Found is used.
    *
    * @param context the <code>Context</code> for this instance
    * @param path this is added to the classpath of the engine
    */ 
   public MapperEngine(Context context, File path) throws IOException{
      this(context, path, null);
   }

   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes.
    * <p>
    * This will load the class byte codes from the supplied path,
    * if they cannot be found within the inherited or system class
    * path. Providing a path other than the <code>Context</code>
    * base path can be useful in seperating services from content.
    * If a service cannot be found an HTTP 404 Not Found is used.
    * <p>
    * This enables an arbitrary object to be issused, which is
    * what is used by this <code>LoaderEngine</code> to load the
    * <code>Service</code> objects. Each instance that is loaded
    * is given this object via its <code>prepare</code> method.
    *
    * @param context the <code>Context</code> for this instance
    * @param path this is added to the classpath of the engine
    * @param data this is the data that is given to each service
    */ 
   public MapperEngine(Context context, File path, Object data) throws IOException{
      this(context, path.getCanonicalFile().toURL(), data);
   }
   
   /**
    * Constructor for the <code>MapperEngine</code>. This creates 
    * an implementation of the <code>LoaderEngine</code> that does
    * not require manual loading of service instances. This allows
    * URI references that contain a service implementation name
    * to load the byte codes.
    * <p>
    * This enables an arbitrary object to be issused, which is
    * what is used by this <code>LoaderEngine</code> to load the
    * <code>Service</code> objects. Each instance that is loaded
    * is given this object via its <code>prepare</code> method.
    *
    * @param context the <code>Context</code> for this instance
    * @param codebase this is added to the classpath of the engine
    * @param data this is the data that is given to each service
    */ 
   private MapperEngine(Context context, URL codebase, Object data) throws IOException{
      super(context, new URL[]{codebase});
      this.mapper = MapperFactory.getInstance(context);
      this.data = data;
   }
   
   /**
    * This will look for and retrieve the requested resource. The 
    * target given must be the actual name for the resource. This 
    * will locate the resource and return the <code>Resource</code>
    * implementation that will handle the target. 
    * <p>
    * This attempts to retrieve a service that has been previously
    * loaded using the <code>load</code> method. If the named
    * service has been previously loaded then the loaded instance
    * is returned from this method. However, if the service name
    * does not corrospond to any previously loaded service then 
    * this will attempt to use the given name to acquire a fully
    * qualified class name to load a service implementation. If 
    * this does not result in sucess, then a report resource that
    * can display the cause and status of the error is returned.
    *
    * @param name this is than name of the service to retrieve
    *
    * @return the service implementation that has been retrieved
    */
   public synchronized Resource lookup(String name) {
      try {   
         String type = mapper.getClass(name);
         
         if(registry.contains(name)) {
            return registry.retrieve(name);
         }
         Object[] list = new Object[] {
            mapper.getConfiguration(name), data};

         if(data == null) {
            list = new Object[] {list[0]};
         }            
         load(name, type, list);
      } catch(Throwable cause) {
         return new Delegate(context, cause);
      }
      return registry.retrieve(name);
   }

   /**
    * This will detetmine whether the URI string issued contains 
    * a reference to a service object. This uses the system wide
    * implementation of the <code>Mapper</code> object to check
    * whether a mapping exists. If a service name can be retrieved
    * from the URI then it is used to load the service. If there 
    * is a failure loading the service then a report is returned.
    * <p>
    * Service objects are loaded the first time they are mapped. 
    * The instance name of a loaded object is used to acquire an
    * implementation class name. This enables multiple service
    * types to be loaded and referenced individually.
    *
    * @param target this is the request URI to be resolved
    *
    * @return the service implementation that has been resolved    
    */
   public synchronized Resource resolve(String target) {
      String path = context.getRequestPath(target);
      String name = mapper.getName(path);

      if(name == null) {
         name = resolver.resolve(path);      
      }
      if(name != null) {
         return lookup(name);
      }
      return new Delegate(context,404); 
   }

   /**
    * This <code>Delegate</code> is used to generate a report of
    * any problems that occur when a service is requested. This 
    * will make use of the <code>Format</code> object to generate
    * formatted reports of errors that occur during a request.
    */ 
   private class Delegate extends Component {

      /**
       * This is the exception that this resource will be report.
       */ 
      private Report report;

      /**
       * Constructor for the <code>Delegate</code> object. This
       * uses a HTTP status code to generate a the  report object
       * describing why the service instance could not be served.
       *
       * @param context this is the context of the mapper engine
       * @param code this is the code that the will be reported
       */
      public Delegate(Context context, int code) { 
         this(context, new StatusReport(code));
      }

      /**
       * Constructor for the <code>Delegate</code> object. This
       * uses a <code>Throwable</code> to generate a report
       * describing why the service instance could not be served.
       *
       * @param context this is the context of the mapper engine
       * @param cause this is the exception used for the report
       */
      public Delegate(Context context, Throwable cause) {
         this(context, new ErrorReport(cause, 500));
      }

      /**
       * Constructor for the <code>Delegate</code> object. This
       * uses the  <code>Report</code> object that will describe
       * an event that has occured while serving the request.
       * 
       * @param context this is the context of the mapper engine
       * @param report this is the report that will be used
       */ 
      public Delegate(Context context, Report report) {
         this.context = context;
         this.report = report;
      }

      /**
       * This will use the provided <code>Throwable</code> to
       * describe the problem retrieving the service. The report
       * is generated using the given <code>Report</code> object.
       *
       * @param req the HTTP request object to be processed
       * @param resp the HTTP response object to be processed
       */  
      protected void process(Request req, Response resp) {
         handle(req, resp, report);
      }
   }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.