The following class, isolates a proxy on it's own thread and using
reflection, isolates it completely from the rest of the executing
environment, by passing method calls as tasks.
Forgive the tab formatting.
Cheers,
Peter.
/*
* 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.river.impl.security.dos;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import net.jini.io.MarshalledInstance;
/**
* A preliminary experiment into Isolating a Smart Proxy.
*
* I think I'll investigate creating a Permission for additional threads
* for improved performance and also creating an event model so clients
* don't need to wait for remote method returns. I'll create a new
interface
* for this, that can be implemented by services directly too.
*
* Alternative, only maintain this data structure for as long as it takes
* to verify proxy trust, then return proxy to the client or have the client
* thread call the smart proxy via the reflection proxy directly.
*
* REMIND: Investigate subclass method returns, this would be simple, if the
* smart proxy is confined to it's own ClassLoader, we just check the class
* of the object returned isn't from that ClassLoader, unless the smart
proxy
* has a ServiceAPISubclassPermission or something like that.
*
* @author Peter Firmstone
*/
public class ProxyIsolate implements InvocationHandler {
private volatile Object smartProxy;
private volatile ExecutorService proxyExecutor;
private volatile Throwable thrown;
private final long timeout;
private final TimeUnit unit;
@SuppressWarnings("unchecked")
public ProxyIsolate(MarshalledInstance proxy,
ClassLoader defaultLoader,
boolean verifyCodebaseIntegrity,
ClassLoader verifierLoader,
Collection context,
long timeout,
TimeUnit timeUnit
) throws TimeoutException, InterruptedException, ExecutionException{
this.timeout = timeout;
unit = timeUnit;
thrown = null;
proxyExecutor = Executors.newSingleThreadExecutor(new Factory(this));
UnmarshallProxyTask task =
new UnmarshallProxyTask(proxy, defaultLoader,
verifyCodebaseIntegrity, verifierLoader, context);
smartProxy = proxyExecutor.submit(task).get(timeout, timeUnit);
}
private Object taskInvoke(Object proxy, Method method, Object[]
args) throws
Exception {
String methodName = method.getName();
if (method.getDeclaringClass() == Object.class) {
// Handle the Object public methods.
if (methodName.equals("hashCode")) {
return new Integer(System.identityHashCode(proxy));
} else if (methodName.equals("equals")) {
return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
} else if (methodName.equals("toString")) {
return proxy.getClass().getName() + '@' +
Integer.toHexString(proxy.hashCode());
}
}
return method.invoke(smartProxy, args);
}
@SuppressWarnings("unchecked")
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (isTerminated()) throw new IOException(thrown);
Callable task = new MethodInvocationTask(this, proxy, method, args);
return proxyExecutor.submit(task).get(timeout, unit);
}
private void terminate(Throwable e){
thrown = e;
smartProxy = null;
proxyExecutor.shutdown();
}
private boolean isTerminated(){
if (smartProxy == null) return true;
return false;
}
private static class Factory implements ThreadFactory{
private static final ThreadGroup tg = new ThreadGroup("IsolatedProxy");
{
tg.setDaemon(true);
tg.setMaxPriority(4);
}
private final ProxyIsolate proxy;
Factory(ProxyIsolate spi){
proxy = spi;
}
public Thread newThread(Runnable r) {
Thread t = new Thread(tg, r);
t.setUncaughtExceptionHandler(new ExceptionHandler(proxy));
return t;
}
}
private static class ExceptionHandler implements
Thread.UncaughtExceptionHandler{
private final ProxyIsolate proxy;
ExceptionHandler(ProxyIsolate spi){
proxy = spi;
}
public void uncaughtException(Thread t, Throwable e) {
// For all other Exceptions we let the ExecutorService handle it.
if ( e instanceof Error){
// Do we want to take different actions based on the error?
proxy.terminate(e);
}
}
}
private static class UnmarshallProxyTask implements Callable{
private final MarshalledInstance mi;
private final ClassLoader defaultLoader;
private final boolean verifyCodebaseIntegrity;
private final ClassLoader verifierLoader;
private final Collection context;
UnmarshallProxyTask(MarshalledInstance proxy,
ClassLoader defaultLoader,
boolean verifyCodebaseIntegrity,
ClassLoader verifierLoader,
Collection context)
{
mi = proxy;
this.defaultLoader = defaultLoader;
this.verifyCodebaseIntegrity = verifyCodebaseIntegrity;
this.verifierLoader = verifierLoader;
this.context = context;
}
public Object call() throws Exception {
return mi.get(defaultLoader, verifyCodebaseIntegrity,
verifierLoader, context);
}
}
private static class MethodInvocationTask implements Callable {
private final ProxyIsolate smartProxy;
private final Object proxy;
private final Method method;
private final Object[] args;
MethodInvocationTask(ProxyIsolate target, Object proxy, Method
method, Object[] args){
smartProxy = target;
this.proxy = proxy;
this.method = method;
this.args = args;
}
public Object call() throws Exception {
return smartProxy.taskInvoke(proxy, method, args);
}
}
}