/*
* 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);
}
}
}
|