/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Java XPCOM Bindings.
*
* The Initial Developer of the Original Code is
* IBM Corporation.
* Portions created by the Initial Developer are Copyright (C) 2004
* IBM Corporation. All Rights Reserved.
*
* Contributor(s):
* Javier Pedemonte (jhpedemonte@gmail.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.xpcom.internal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.mozilla.xpcom.XPCOMException;
/**
* This class is used to pass XPCOM objects to Java functions. A
* java.lang.reflect.Proxy
instance is created using the expected
* interface, and all calls to the proxy are forwarded to the XPCOM object.
*/
public class XPCOMJavaProxy implements InvocationHandler {
/**
* Pointer to the XPCOM object for which we are a proxy.
*/
protected long nativeXPCOMPtr;
/**
* Default constructor.
*
* @param aXPCOMInstance address of XPCOM object as a long
*/
public XPCOMJavaProxy(long aXPCOMInstance) {
nativeXPCOMPtr = aXPCOMInstance;
}
/**
* Returns the XPCOM object that the given proxy references.
*
* @param aProxy Proxy created by createProxy
*
* @return address of XPCOM object as a long
*/
protected static long getNativeXPCOMInstance(Object aProxy) {
XPCOMJavaProxy proxy = (XPCOMJavaProxy) Proxy.getInvocationHandler(aProxy);
return proxy.nativeXPCOMPtr;
}
/**
* Creates a Proxy for the given XPCOM object.
*
* @param aInterface interface from which to create Proxy
* @param aXPCOMInstance address of XPCOM object as a long
*
* @return Proxy of given XPCOM object
*/
protected static Object createProxy(Class aInterface, long aXPCOMInstance) {
// XXX We should really get the class loader from |aInterface|. However,
// that class loader doesn't know about |XPCOMJavaProxyBase|. So for
// now, we get the class loader that loaded |XPCOMJavaProxy|. When
// we get rid of the "XPCOMJavaProxyBase.java" class, we can revert
// to the old method below.
// return Proxy.newProxyInstance(aInterface.getClassLoader(),
return Proxy.newProxyInstance(XPCOMJavaProxy.class.getClassLoader(),
new Class[] { aInterface, XPCOMJavaProxyBase.class },
new XPCOMJavaProxy(aXPCOMInstance));
}
/**
* All calls to the Java proxy are forwarded to this method. This method
* takes care of a few of the Object
method calls; all other
* calls are forwarded to the XPCOM object.
*
* @param aProxy Proxy created by createProxy
* @param aMethod object that describes the called method
* @param aParams array of the arguments passed to the method
*
* @return return value as defined by given aMethod
*/
public Object invoke(Object aProxy, Method aMethod, Object[] aParams)
throws Throwable {
String methodName = aMethod.getName();
// Handle the three java.lang.Object methods that are passed to us.
if (aMethod.getDeclaringClass() == Object.class) {
if (methodName.equals("hashCode")) {
return proxyHashCode(aProxy);
}
if (methodName.equals("equals")) {
return proxyEquals(aProxy, aParams[0]);
}
if (methodName.equals("toString")) {
return proxyToString(aProxy);
}
System.err.println("WARNING: Unhandled Object method [" +
methodName + "]");
return null;
}
// Handle the 'finalize' method called during garbage collection
if (aMethod.getDeclaringClass() == XPCOMJavaProxyBase.class) {
if (methodName.equals("finalize")) {
finalizeProxy(aProxy);
} else {
System.err.println("WARNING: Unhandled XPCOMJavaProxyBase method [" +
methodName + "]");
}
return null;
}
// If not already handled, pass method calls to XPCOM object.
return callXPCOMMethod(aProxy, methodName, aParams);
}
/**
* Handles method calls of java.lang.Object.hashCode
*
* @param aProxy Proxy created by createProxy
*
* @return Integer object representing hash code of given object
*
* @see Object#hashCode()
*/
protected static Integer proxyHashCode(Object aProxy) {
return new Integer(System.identityHashCode(aProxy));
}
/**
* Handles method calls of java.lang.Object.equals
*
* @param aProxy Proxy created by createProxy
* @param aOther another object
*
* @return true
if the given objects are the same;
* false
otherwise
*
* @see Object#equals(Object)
*/
protected static Boolean proxyEquals(Object aProxy, Object aOther) {
// See if the two are the same Java object
if (aProxy == aOther) {
return Boolean.TRUE;
} else {
// If not, then see if they represent the same XPCOM object. But first,
// we need to check if |aOther| is an XPCOMJavaProxy.
if (isXPCOMJavaProxy(aOther) && isSameXPCOMObject(aProxy, aOther)) {
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
/**
* Indicates whether the given object is an XPCOMJavaProxy.
*
* @param aObject object to check
*
* @return true
if the given object is an XPCOMJavaProxy;
* false
otherwise
*/
protected static boolean isXPCOMJavaProxy(Object aObject) {
if (aObject != null && Proxy.isProxyClass(aObject.getClass())) {
InvocationHandler h = Proxy.getInvocationHandler(aObject);
if (h instanceof XPCOMJavaProxy) {
return true;
}
}
return false;
}
/**
* Checks if the two given XPCOMJavaProxy objects are proxies for
* the same XPCOM object.
*
* @param aProxy1 XPCOMJavaProxy created by createProxy
* @param aProxy2 XPCOMJavaProxy created by createProxy
*
* @return true
if both proxies represent the same XPCOM object;
* false
otherwise
*/
protected static native boolean isSameXPCOMObject(Object aProxy1,
Object aProxy2);
/**
* Handles method calls of java.lang.Object.toString
*
* @param aProxy Proxy created by createProxy
*
* @return String representation of given object
*
* @see Object#toString()
*/
protected static String proxyToString(Object aProxy) {
return aProxy.getClass().getInterfaces()[0].getName() + '@' +
Integer.toHexString(aProxy.hashCode());
}
/**
* Called when the proxy is garbage collected by the JVM. Allows us to clean
* up any references to the XPCOM object.
*
* @param aProxy reference to Proxy that is being garbage collected
*/
protected void finalizeProxy(Object aProxy) throws Throwable {
finalizeProxyNative(aProxy);
super.finalize();
}
protected static native void finalizeProxyNative(Object aProxy);
/**
* Calls the XPCOM object referenced by the proxy with the given method.
*
* @param aProxy Proxy created by createProxy
* @param aMethodName name of method that we want to call
* @param aParams array of params passed to method
*
* @return return value as defined by given method
*
* @exception XPCOMException if XPCOM method failed. Values of XPCOMException
* are defined by the method called.
*/
protected static native Object callXPCOMMethod(Object aProxy,
String aMethodName, Object[] aParams);
}