/* Copyright (c) 2014 Dr David H. Akehurst (itemis), All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna.platform.win32.COM.util;
import com.sun.jna.platform.win32.COM.IDispatch;
import com.sun.jna.platform.win32.COM.IDispatchCallback;
import com.sun.jna.platform.win32.COM.util.annotation.ComObject;
import com.sun.jna.platform.win32.Guid;
import com.sun.jna.platform.win32.OaIdl;
import com.sun.jna.platform.win32.OleAuto;
import com.sun.jna.platform.win32.Variant;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/**
* Factory is intended as a simpler to use version of ObjectFactory.
*
* <p>The Factory abstracts the necessity to handle COM threading by introducing
* a dispatching thread, that is correctly COM initialized and is used to handle
* all outgoing calls.</p>
*
* <p><b>NOTE:</b> Remember to call factory.getComThread().terminate() at some
* appropriate point, when the factory is not used anymore</p>
*/
public class Factory extends ObjectFactory {
private ComThread comThread;
public Factory() {
this(new ComThread("Default Factory COM Thread", 5000, new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
//ignore
}
}));
}
public Factory(ComThread comThread) {
this.comThread = comThread;
}
private class ProxyObject2 implements InvocationHandler {
private final Object delegate;
public ProxyObject2(Object delegate) {
this.delegate = delegate;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if (args != null) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null
&& Proxy.isProxyClass(args[i].getClass())) {
InvocationHandler ih = Proxy.getInvocationHandler(args[i]);
if (ih instanceof ProxyObject2) {
args[i] = ((ProxyObject2) ih).delegate;
}
}
}
}
return comThread.execute(new Callable<Object>() {
@Override
public Object call() throws Exception {
return method.invoke(delegate, args);
}
});
}
}
private class CallbackProxy2 extends CallbackProxy {
public CallbackProxy2(ObjectFactory factory, Class<?> comEventCallbackInterface, IComEventCallbackListener comEventCallbackListener) {
super(factory, comEventCallbackInterface, comEventCallbackListener);
}
@Override
public WinNT.HRESULT Invoke(OaIdl.DISPID dispIdMember, Guid.REFIID riid, WinDef.LCID lcid, WinDef.WORD wFlags, OleAuto.DISPPARAMS.ByReference pDispParams, Variant.VARIANT.ByReference pVarResult, OaIdl.EXCEPINFO.ByReference pExcepInfo, IntByReference puArgErr) {
// Mark callbacks as COM initialized - so normal inline call
// invocation can be used -- see ComThread#
ComThread.setComThread(true);
try {
return super.Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
} finally {
ComThread.setComThread(false);
}
}
}
@Override
public <T> T createProxy(Class<T> comInterface, IDispatch dispatch) {
T result = super.createProxy(comInterface, dispatch);
ProxyObject2 po2 = new ProxyObject2(result);
Object proxy = Proxy.newProxyInstance(comInterface.getClassLoader(), new Class<?>[]{comInterface}, po2);
return (T) proxy;
}
@Override
Guid.GUID discoverClsId(final ComObject annotation) {
return runInComThread(new Callable<Guid.GUID>() {
public Guid.GUID call() throws Exception {
return Factory.super.discoverClsId(annotation);
}
});
}
@Override
public <T> T fetchObject(final Class<T> comInterface) {
// Proxy2 is added by createProxy inside fetch Object
return runInComThread(new Callable<T>() {
public T call() throws Exception {
return Factory.super.fetchObject(comInterface);
}
});
}
@Override
public <T> T createObject(final Class<T> comInterface) {
// Proxy2 is added by createProxy inside fetch Object
return runInComThread(new Callable<T>() {
public T call() throws Exception {
return Factory.super.createObject(comInterface);
}
});
}
@Override
IDispatchCallback createDispatchCallback(Class<?> comEventCallbackInterface, IComEventCallbackListener comEventCallbackListener) {
return new CallbackProxy2(this, comEventCallbackInterface, comEventCallbackListener);
}
@Override
public IRunningObjectTable getRunningObjectTable() {
return super.getRunningObjectTable();
}
private <T> T runInComThread(Callable<T> callable) {
try {
return comThread.execute(callable);
} catch (TimeoutException ex) {
throw new RuntimeException(ex);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
} catch (ExecutionException ex) {
throw new RuntimeException(ex);
}
}
public ComThread getComThread() {
return comThread;
}
}