Skip to content

Commit

Permalink
ARQ-678 Handle NoClassDefFoundException in ExceptionProxy
Browse files Browse the repository at this point in the history
In cases where we have the Exception class on classpath, but the Class is refering to a unknown class.
  • Loading branch information
aslakknutsen committed Dec 14, 2011
1 parent 9d3b82f commit 116f59e
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 147 deletions.
156 changes: 13 additions & 143 deletions test/spi/src/main/java/org/jboss/arquillian/test/spi/ExceptionProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

/**
* Takes an exception class and creates a proxy that can be used to rebuild the
Expand Down Expand Up @@ -117,127 +116,6 @@ public ArquillianProxyException createProxyException(String reason)
return exception;
}

/**
* Constructs an instance of the passed in class if a suitable constructor
* is found. If the constructor subclasses {@link Throwable} it is returned.
* If a Class instance is not constructed ot is not a {@link Throwable} then
* another exception is returned indicating this.
*
* @param clazz
* Class to construct
* @return Instance of the Throwable class.
*/
private Throwable constructExceptionForClass(Class<?> clazz)
{
Object object = buildObjectFromClassConstructors(clazz);

if (object == null)
{
return createProxyException("Could not find suitable constructor");
}

if (!(object instanceof Throwable))
{
return createProxyException("Proxy references non-Throwable type");
}
return (Throwable) object;
}

private Object buildObjectFromClassConstructors(Class<?> clazz)
{
// try the (String,Throwable) constructor first
Object object = buildExceptionFromConstructor(clazz, new Class<?>[] {String.class, Throwable.class}, new Object[] {message, getCause()});
if (object != null)
{
return object;
}

// try the (String,Exception) constructor
object = buildExceptionFromConstructor(clazz, new Class<?>[] {String.class, Exception.class}, new Object[] {message, getCause()});
if (object != null)
{
return object;
}

// try the (Throwable) constructor
object = buildExceptionFromConstructor(clazz, new Class<?>[]{Throwable.class}, new Object[] {getCause()});
if (object != null)
{
return object;
}

// try the (Exception) constructor
object = buildExceptionFromConstructor(clazz, new Class<?>[]{Exception.class}, new Object[] {getCause()});
if (object != null)
{
return object;
}

// try the (Object) constructor
object = buildExceptionFromConstructor(clazz, new Class<?>[]{Object.class}, new Object[] {message});
if (object != null)
{
return object;
}

// try the (String, Object) constructor
object = buildExceptionFromConstructor(clazz, new Class<?>[]{String.class}, new Object[] {message});
if (object != null)
{
return object;
}

return null;
}

/**
* Attempt to build an exception of the given class type using the
* constructor signature and parameters passed in. If no constructor matches
* or the constructor throws an exception, then null is returned.
*
* @param clazz
* Class to construct
* @param signature
* Array of class types to match the signature on
* @param params
* Parameter values to pass to the constructor if found.
* @return The object instance created using the constructor
*/
private <T> T buildExceptionFromConstructor(Class<T> clazz, Class<?>[] signature, Object[] params) {
Constructor<?> constructor = null;
// try the message,cause constructor first
Class<?> nextSource = clazz;
// while (nextSource != Object.class && constructor == null)
// {
try
{
constructor = nextSource.getDeclaredConstructor(signature);
}
catch (SecurityException e)
{
// we'll try the next signature
}
catch (NoSuchMethodException e)
{
// we'll try the next signature
}
// nextSource = nextSource.getSuperclass();
// }
// if we found a working constructor, use it
if (constructor != null) {
try {
@SuppressWarnings("unchecked")
T result = (T) constructor.newInstance(params);
return result;
} catch (Throwable e) {
return null;
}
}
// no matching constructor, no result
return null;

}

/**
* Static method to create an exception proxy for the passed in
* {@link Throwable} class. If null is passed in, null is returned as the
Expand Down Expand Up @@ -302,29 +180,21 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept
try
{
ByteArrayInputStream originalIn = new ByteArrayInputStream(originalExceptionData);
ObjectInputStream input = new ObjectInputStream(originalIn) ;
/*// need to active for testing
{
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException
{
return Class.forName(desc.getName(), false, Thread.currentThread().getContextClassLoader());
}
};
*/
ObjectInputStream input = new ObjectInputStream(originalIn);
// // Uncomment to run ExceptionProxySerializationTestCase
// {
// @Override
// protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException
// {
// return Class.forName(desc.getName(), false, Thread.currentThread().getContextClassLoader());
// }
// };
original = (Throwable)input.readObject();
try
{
// reset the cause, so we can de-serialize them individual
SecurityActions.setFieldValue(Throwable.class, original, "cause", causeProxy.createException());
}
catch (Exception e)
{
// move on, try to serialize anyway
}

// reset the cause, so we can de-serialize them individual
SecurityActions.setFieldValue(Throwable.class, original, "cause", causeProxy.createException());
}
catch (ClassNotFoundException e)
catch (Throwable e) // ClassNotFoundExcpetion / NoClassDefFoundError
{
// ignore, could not load class on client side, move on and create a fake 'proxy' later
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
public class ExceptionProxySerializationTestCase
{

@Test @Ignore // not ready for automation
@Test @Ignore // not ready for automation, uncomment ObjectInputStream override in ExceptionProxy.readExternal to run
public void shouldBeAbleToDeserialize() throws Exception
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
Expand Down Expand Up @@ -66,10 +66,14 @@ public void shouldBeAbleToDeserialize() throws Exception
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
if(UnknownException.class.getName().equals(name))
if(UnknownException.class.getName().equals(name))
{
return null;
}
if(UnknownObject.class.getName().equals(name))
{
throw new NoClassDefFoundError(name);
}
return super.loadClass(name, resolve);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private void proxy(Throwable throwable) throws Throwable
/**
* @param throwable
*/
@SuppressWarnings("unused")
private void printConstructors(Throwable throwable) throws Exception
{
System.out.println("Declared-Constrcutors for: " + throwable.getClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
*/
public class UnknownException extends Exception
{
private static final long serialVersionUID = 1L;

private static final UnknownObject serialVersionUID = new UnknownObject();
public UnknownException(Throwable cause)
{
super(cause);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.jboss.arquillian.test.spi;

import java.io.Serializable;

/**
* UnknownObject
*
* @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
* @version $Revision: $
*/
public class UnknownObject implements Serializable
{

}

0 comments on commit 116f59e

Please sign in to comment.