/** | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (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.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.apache.aries.ejb.openejb.extender; | |
import java.io.Serializable; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Properties; | |
import java.util.WeakHashMap; | |
import java.util.concurrent.Callable; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
import java.util.concurrent.atomic.AtomicReference; | |
import org.apache.aries.proxy.InvocationListener; | |
import org.apache.aries.proxy.ProxyManager; | |
import org.apache.aries.proxy.UnableToProxyException; | |
import org.apache.aries.util.tracker.SingleServiceTracker; | |
import org.apache.aries.util.tracker.SingleServiceTracker.SingleServiceListener; | |
import org.apache.openejb.OpenEJBException; | |
import org.apache.openejb.util.proxy.InvocationHandler; | |
import org.apache.openejb.util.proxy.ProxyFactory; | |
import org.osgi.framework.BundleContext; | |
public class AriesProxyService implements ProxyFactory, SingleServiceListener { | |
private static class NoProxySupportException extends RuntimeException { | |
public NoProxySupportException() { | |
super("No Proxy support is available"); | |
} | |
} | |
private static final class InvocationHandlerProxy implements Callable<Object>, InvocationListener { | |
private final InvocationHandler handler; | |
private final Map<Thread, Class<?>> invocations = new ConcurrentHashMap<Thread, Class<?>>(); | |
private final ConcurrentMap<Class<?>, Object> proxys = new ConcurrentHashMap<Class<?>, Object>(); | |
public InvocationHandlerProxy(InvocationHandler handler) { | |
this.handler = handler; | |
} | |
public InvocationHandler getHandler() { | |
return handler; | |
} | |
public void postInvoke(Object arg0, Object arg1, Method arg2, Object arg3) | |
throws Throwable { | |
// No op | |
} | |
public void postInvokeExceptionalReturn(Object arg0, Object arg1, | |
Method arg2, Throwable arg3) throws Throwable { | |
//No op | |
} | |
public Object preInvoke(Object arg0, Method arg1, Object[] arg2) | |
throws Throwable { | |
invocations.put(Thread.currentThread(), arg1.getDeclaringClass()); | |
return null; | |
} | |
public Object call() throws Exception { | |
Class<?> c = invocations.remove(Thread.currentThread()); | |
if(c == null) | |
throw new IllegalStateException("Unable to establish any context"); | |
else if (c.equals(Object.class)) { | |
//This is a toString or similar, just use an interface we know | |
//we can see and that doesn't have any methods on it :) | |
c = Serializable.class; | |
} | |
Object proxy = proxys.get(c); | |
if(proxy == null) { | |
Object tmp = Proxy.newProxyInstance(c.getClassLoader(), new Class[] {c}, handler); | |
proxy = proxys.putIfAbsent(c, tmp); | |
if(proxy == null) | |
proxy = tmp; | |
} | |
return proxy; | |
} | |
} | |
private static class InnerProxyDelegator implements Callable<Object> { | |
private final Object delegate; | |
public InnerProxyDelegator(Object delegate) { | |
this.delegate = delegate; | |
} | |
public Object call() { | |
return delegate; | |
} | |
} | |
private final Map<Class<?>, Object> proxies = Collections.synchronizedMap( | |
new WeakHashMap<Class<?>, Object>()); | |
private final SingleServiceTracker<ProxyManager> proxyTracker; | |
private AriesProxyService(BundleContext ctx) { | |
proxyTracker = new SingleServiceTracker<ProxyManager>(ctx, ProxyManager.class, this); | |
proxyTracker.open(); | |
} | |
private final AtomicReference<ProxyManager> manager = | |
new AtomicReference<ProxyManager>(); | |
private static final AtomicReference<AriesProxyService> INSTANCE = | |
new AtomicReference<AriesProxyService>(); | |
private final ProxyManager getManager() { | |
ProxyManager pManager = manager.get(); | |
if(pManager == null) { | |
throw new NoProxySupportException(); | |
} | |
return pManager; | |
} | |
public static AriesProxyService get() { | |
return INSTANCE.get(); | |
} | |
public static void init(BundleContext ctx) { | |
AriesProxyService oTM = new AriesProxyService(ctx); | |
if(!!!INSTANCE.compareAndSet(null, oTM)) | |
oTM.destroy(); | |
} | |
public void destroy() { | |
INSTANCE.set(null); | |
proxyTracker.close(); | |
} | |
public void serviceFound() { | |
update(); | |
} | |
public void serviceLost() { | |
update(); | |
} | |
public void serviceReplaced() { | |
update(); | |
} | |
private void update() { | |
manager.set(proxyTracker.getService()); | |
} | |
public InvocationHandler getInvocationHandler(Object arg0) | |
throws IllegalArgumentException { | |
Callable<Object> unwrapped = getManager().unwrap(arg0); | |
if(unwrapped instanceof InnerProxyDelegator) { | |
unwrapped = getManager().unwrap(((InnerProxyDelegator)unwrapped).call()); | |
} | |
if(unwrapped instanceof InvocationHandlerProxy) { | |
return ((InvocationHandlerProxy) unwrapped).getHandler(); | |
} | |
return null; | |
} | |
public Class getProxyClass(Class iface) throws IllegalArgumentException { | |
if(iface == null || !!!iface.isInterface()) | |
throw new IllegalArgumentException("Not an interface " + iface); | |
return newProxyInstance(iface, null).getClass(); | |
} | |
public Class getProxyClass(Class[] ifaces) throws IllegalArgumentException { | |
if(ifaces == null || ifaces.length == 0) | |
throw new IllegalArgumentException("No interfaces."); | |
for(Class iface : ifaces) { | |
if (!!!iface.isInterface()) | |
throw new IllegalArgumentException("Not an interface " + iface + " in " + Arrays.toString(ifaces)); | |
} | |
return newProxyInstance(ifaces, null).getClass(); | |
} | |
public void init(Properties arg0) throws OpenEJBException { | |
//No op | |
} | |
public boolean isProxyClass(Class arg0) { | |
return proxies.containsKey(arg0); | |
} | |
public Object newProxyInstance(Class iface, InvocationHandler arg1) | |
throws IllegalArgumentException { | |
return newProxyInstance(new Class[] {iface}, arg1); | |
} | |
public Object newProxyInstance(Class[] ifaces, InvocationHandler arg1) | |
throws IllegalArgumentException { | |
InvocationHandlerProxy ihProxy = new InvocationHandlerProxy(arg1); | |
List<Class<?>> classes = new ArrayList<Class<?>>(); | |
for(Class<?> iface : ifaces) | |
classes.add(iface); | |
try { | |
Object inner = getManager().createDelegatingProxy(null, classes, ihProxy, null); | |
Object proxy = getManager().createDelegatingInterceptingProxy(null, classes, | |
new InnerProxyDelegator(inner), null, ihProxy); | |
proxies.put(proxy.getClass(), null); | |
return proxy; | |
} catch (UnableToProxyException e) { | |
throw new IllegalArgumentException(e); | |
} | |
} | |
} |