JucasEventHandler.java :  » Web-Framework » jucas » org » jucas » Java Open Source

Java Open Source » Web Framework » jucas 
jucas » org » jucas » JucasEventHandler.java
/*
 * License
 *
 *
 * Copyright (c) 2003 Essl Christian.  All rights 
 * reserved.
 * 
 * This Licence is based on the Apache Software Licence Version 1.1.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by Christian Essl 
 *        and others for project Jucas (http://www.jucas.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Jucas" and "Christian Essl" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact essl_christian@jucas.org.
 *
 * 5. Products derived from this software may not be called "Jucas"
 *    or "Christian Essl", nor may "Jucas" or "Christian Essl"
 *    appear in their name, without prior written permission
 *    of Christian Essl (jucas@esslchristian.de).
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL CHRISTIAN ESSL OR
 * OTHER CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * 
 */
package org.jucas;

import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;

/**
 * A utility class which is used to script an JavaBeans style eventHandler through JavaScript.
 * Similar to java.beans.EventHandler but because of JavaScript it is more powerful.
 * @author chris
 */
public class JucasEventHandler implements InvocationHandler, Serializable
{
  private static Method EQUALS;
  static {
    try
    {
      EQUALS = Object.class.getMethod("equals", new Class[] { Object.class });
    } catch (NoSuchMethodException e)
    {
    }
  }

  ////////////////
  //crators

  /**
   * Creates an EventListener for the given listener interface
   * @param cL the Classloader in which the interface is defined
   * @param listenerInterface the ListenerInterface the returned object should implemnt
   * @param script the java script to be executed if event happens
   * @param target the target is either given as variable in or if it implements IScriptEnabled
   *                is used to delegate the script exection to (May be null)
   * @param action the method name on which the script should be executed (if null the script will be executed for each method
   *                 in the Listener)
   * @return Object the listener the listener implementing the eventListener
   * @throws Exception if the script can not be compiled
   */
  public static Object create(
    ClassLoader cL,
    Class listenerInterface,
    String script,
    Object target,
    String action)
  {
    Script jScript;
    try
    {
      jScript = JavaScriptUtils.createScript(script, "EventHandler");
    } catch (Exception e)
    {
      throw new IllegalArgumentException("The script is not valid");
    }
    return create(cL, listenerInterface, jScript, target, action);
  }

  public static Object create(
    ClassLoader cL,
    Class listenerInterface,
    Script script,
    Object target,
    String action)
  {
    if (listenerInterface == null || !listenerInterface.isInterface())
      throw new IllegalArgumentException(
        "Must provide a listenerInterface: " + listenerInterface);
    if (script == null)
      throw new NullPointerException("No script");
    if (cL == null)
      cL = JucasEventHandler.class.getClassLoader();
    JucasEventHandler jucasEventH = new JucasEventHandler(script, target, action);

    Object ret = Proxy.newProxyInstance(cL, new Class[] { listenerInterface }, jucasEventH);
    return ret;
  }

  /**
   * like {@link #create(ClassLoader, Class, String, Object, String)}. But takes the classLoader
   * of this class
   * @param listenerInterface
   * @param script
   * @param target
   * @param action
   * @return Object
   */
  public static Object create(
    Class listenerInterface,
    String script,
    Object target,
    String action)
  {
    return create(null, listenerInterface, script, target, action);
  }

  /**
   * creates a Listener without a target
   * @param listenerInterface
   * @param script
   * @param action
   * @return Object
   */
  public static Object create(Class listenerInterface, String script, String action)
  {
    return create(null, listenerInterface, script, null, action);
  }

  /**
   * creates a Listener without an action
   * @param listenerInterface
   * @param script
   * @param target
   * @return Object
   */
  public static Object create(Class listenerInterface, String script, Object target)
  {
    return create(null, listenerInterface, script, target, null);
  }

  /**
   * creates Listener without an target and an action
   * @param listenerInterface
   * @param script
   * @return Object
   */
  public static Object create(Class listenerInterface, String script) throws Exception
  {
    return create(null, listenerInterface, script, null, null);
  }

  /**
   * Cretes a Listener where the EventDispatcher (the object firing the event) and the eventName as specified
   * by the JavaBeans specification is given.
   * @param eventDispatcher the object firing the event
   * @param eventName the name of the event according to JavaBeans spec
   * @param script the script to execute
   * @param target the target is either given as variable in or if it implements IScriptEnabled
   *                is used to delegate the script exection to (May be null)
   * @param action the method name on which the script should be executed (if null the script will be executed for each method
   *                 in the Listener)
   * @return Object the listener the listener implementing the eventListener
   */
  public static Object create(
    Object eventDispatcher,
    String eventName,
    String script,
    Object target,
    String action)
  {
    //find the listener class
    try
    {
      BeanInfo info = Introspector.getBeanInfo(eventDispatcher.getClass());
      EventSetDescriptor[] descs = info.getEventSetDescriptors();
      for (int i = 0; i < descs.length; i++)
      {
        if (descs[i].getName().equals(eventName))
        {
          Class listenerClazz = descs[i].getListenerType();
          if (listenerClazz.isInterface())
            return create(listenerClazz, script, target, action);
        }
      }
    } catch (IntrospectionException e)
    {
      throw new IllegalArgumentException("Error with EventDispatcher: " + e);
    }
    throw new IllegalArgumentException("No event for eventName: " + eventName);
  }

  public static void addListenerTo(
    Object eventDispatcher,
    String eventName,
    String script,
    Object target,
    String action)
    throws JucasEventHandlerException
  {
    Script jScript;
    try
    {
      jScript = JavaScriptUtils.createScript(script, "EventHandler");
    } catch (Exception e)
    {
      throw new IllegalArgumentException("The script is not valid");
    }
    addListenerTo(eventDispatcher,eventName,jScript,target,action);    
  }
  /**
   * like {@link #create(Object, String, String, Object, String)} but also registers
   * the listener with the eventDispatcher
   * @param eventDispatcher
   * @param eventName
   * @param script
   * @param target
   * @param action
   * @throws JucasEventHandlerException
   */
  public static void addListenerTo(
    Object eventDispatcher,
    String eventName,
    Script script,
    Object target,
    String action)
    throws JucasEventHandlerException
  {
    //find the listener class
    Object listener = null;
    Method addMethod = null;
    try
    {
      BeanInfo info = Introspector.getBeanInfo(eventDispatcher.getClass());
      EventSetDescriptor[] descs = info.getEventSetDescriptors();
      for (int i = 0; i < descs.length; i++)
      {
        if (descs[i].getName().equals(eventName))
        {
          Class listenerClazz = descs[i].getListenerType();
          if (listenerClazz.isInterface())
          {
            listener =
              create(
                JucasEventHandler.class.getClassLoader(),
                listenerClazz,
                script,
                target,
                action);
            addMethod = descs[i].getAddListenerMethod();
            break;
          }
        }
      }
    } catch (IntrospectionException e)
    {
      throw new JucasEventHandlerException(
        "Error in introspecting eventDsipatcher: " + eventDispatcher + ":" + e);
    }

    if (listener == null || addMethod == null)
    {
      throw new JucasEventHandlerException("Could not get listener for event: " + eventName);
    }

    try
    {
      addMethod.invoke(eventDispatcher, new Object[] { listener });
    } catch (Exception e)
    {
      throw new JucasEventHandlerException("Could not add EventHandler: " + e);
    }

  }

  //////////////////////
  //instance things

  private Object target;
  private String action;
  private Script script;
  /**
  * Constructor for JucasEventHandler.
  */
  private JucasEventHandler(Script script, Object target, String action)
  {
    super();
    if (script == null)
      throw new IllegalArgumentException("must provide target and action");
    this.target = target;
    this.action = action;
    this.script = script;
  }

  /**
   * java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.
   * reflect.Method, java.lang.Object)
   */
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  {
    //check if is equals method
    if (EQUALS.equals(method))
    {
      Object arg = args[0];
      if (arg == null
        || Proxy.isProxyClass(arg.getClass())
        || !(Proxy.getInvocationHandler(arg) instanceof JucasEventHandler))
        return Boolean.FALSE;

      JucasEventHandler invo = (JucasEventHandler) Proxy.getInvocationHandler(arg);

      boolean ret;
      if (target == null)
        ret = invo.target == null;
      else
        ret = target.equals(invo.target);
      if (!ret)
        return Boolean.FALSE;

      if (action == null)
        ret = invo.action == null;
      else
        ret = action.equals(invo.action);
      if (!ret)
        return Boolean.FALSE;

      if (script.equals(invo.script))
        return Boolean.TRUE;
      else
        return Boolean.FALSE;
    }

    //only do if the actions fits
    if (action != null && !action.equals(method.getName()))
      return null;

    //check the target
    Object target = this.target;
    if (target != null && target instanceof BeanLink)
    {
      target = ((BeanLink) target).getBean();
    }

    //we have two possibilities to execute the script
    //either directly on a ScriptEnabled
    //or we use BeanShell
    if (target != null && target instanceof IScriptEnabled)
    {
      IScriptEnabled scriptTarget = (IScriptEnabled) target;
      scriptTarget.evaluateScript(script, args);
    } else
    {

      Context cx = Context.enter();
      Scriptable scope = cx.initStandardObjects(null);
      JavaScriptUtils.setEventVars(args, target, scope);
      this.script.exec(cx, scope);
      Context.exit();

    }

    return null;
  }

  public static interface IScriptEnabled
  {
    public void evaluateScript(Script script, Object args[]) throws Exception;
  }

  public static class JucasEventHandlerException extends RuntimeException
  
  {

    /**
     * Constructor for JucasEventHandlerException.
     * @param message
     */
    public JucasEventHandlerException(String message)
    {
      super(message);
    }

  }
}
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.