Refs #1004 Rework remote exception handling

This commit is contained in:
Konstantin Krivopustov 2011-11-22 08:22:26 +00:00
parent ee29619411
commit 17084c8999
9 changed files with 83 additions and 56 deletions

View File

@ -60,6 +60,11 @@ public class TestingServiceBean implements TestingService {
return b;
}
@Override
public String executeWithException() throws TestException {
throw new TestException("an error");
}
@Override
public void clearScheduledTasks() {
Transaction tx = persistence.createTransaction();

View File

@ -6,6 +6,8 @@
package com.haulmont.cuba.core.app;
import com.haulmont.cuba.core.global.SupportedByClient;
/**
* Service interface for integration testing. Don't use it in application code!
*
@ -23,8 +25,18 @@ public interface TestingService {
boolean primitiveParameters(boolean b, int i, long l, double d);
String executeWithException() throws TestException;
/**
* Warning! Removes all scheduled tasks from the database!
*/
void clearScheduledTasks();
@SupportedByClient
public static class TestException extends Exception {
public TestException(String message) {
super(message);
}
}
}

View File

@ -5,7 +5,6 @@
*/
package com.haulmont.cuba.core.global;
import com.haulmont.cuba.core.sys.ClassesInfo;
import com.haulmont.cuba.security.entity.PermissionType;
/**
@ -15,14 +14,11 @@ import com.haulmont.cuba.security.entity.PermissionType;
*
* @author krivopustov
*/
@SupportedByClient
public class AccessDeniedException extends RuntimeException
{
private static final long serialVersionUID = -3097861878301424338L;
static {
ClassesInfo.addClientSupported(AccessDeniedException.class);
}
private PermissionType type;
private String target;

View File

@ -6,7 +6,6 @@
package com.haulmont.cuba.core.global;
import com.haulmont.cuba.core.sys.ClassesInfo;
import org.apache.commons.lang.exception.ExceptionUtils;
import javax.annotation.Nullable;
@ -18,7 +17,7 @@ import java.util.List;
/**
* Exception that returns to clients from the middleware. Contains the information about the whole server-side
* exception chain in the <code>Cause</code> objects list. Actual exception instances are included only if they
* explicitly made available for the clients (registered in {@link ClassesInfo}).
* explicitly declared as available for the clients (annotated with {@link SupportedByClient}).
*
* <p>$Id$</p>
*
@ -35,7 +34,7 @@ public class RemoteException extends RuntimeException {
public Cause(Throwable throwable) {
className = throwable.getClass().getName();
message = throwable.getMessage();
if (ClassesInfo.isClientSupported(throwable.getClass()))
if (throwable.getClass().getAnnotation(SupportedByClient.class) != null)
this.throwable = throwable;
}
@ -72,6 +71,19 @@ public class RemoteException extends RuntimeException {
return Collections.unmodifiableList(causes);
}
/**
* @return First exception in the causes list if it is checked, null otherwise
*/
public Exception getFirstCheckedException() {
if (!causes.isEmpty()) {
Throwable t = causes.get(0).getThrowable();
if (t != null && !(t instanceof RuntimeException) && !(t instanceof Error)) {
return (Exception) t;
}
}
return null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("RemoteException:");

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
package com.haulmont.cuba.core.global;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that marks a class as available on the client side.
*
* <p>$Id$</p>
*
* @author krivopustov
*/
@Target({java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportedByClient {
}

View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
package com.haulmont.cuba.core.sys;
import java.util.ArrayList;
import java.util.List;
/**
* Class that provides information about classes availability for different application layers.
*
* <p>$Id$</p>
*
* @author krivopustov
*/
public class ClassesInfo {
private static List<Class> clientSupported = new ArrayList<Class>();
/**
* Register a class as available for the client layer.
* @param aClass class
*/
public static synchronized void addClientSupported(Class aClass) {
if (!clientSupported.contains(aClass))
clientSupported.add(aClass);
}
/**
* Check whether the class is available for the client layer.
* @param aClass class
* @return true if available
*/
public static synchronized boolean isClientSupported(Class aClass) {
return clientSupported.contains(aClass);
}
}

View File

@ -6,7 +6,11 @@
package com.haulmont.cuba.core.sys.remoting;
import com.haulmont.cuba.core.global.RemoteException;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
import org.springframework.remoting.support.RemoteInvocationResult;
import java.lang.reflect.InvocationTargetException;
/**
* <p>$Id$</p>
@ -23,4 +27,19 @@ public class HttpServiceProxy extends HttpInvokerProxyFactoryBean {
executor.setBeanClassLoader(getBeanClassLoader());
setHttpInvokerRequestExecutor(executor);
}
@Override
protected Object recreateRemoteInvocationResult(RemoteInvocationResult result) throws Throwable {
Throwable throwable = result.getException();
if (throwable != null) {
if (throwable instanceof InvocationTargetException)
throwable = ((InvocationTargetException) throwable).getTargetException();
if (throwable instanceof RemoteException) {
Exception exception = ((RemoteException) throwable).getFirstCheckedException();
if (exception != null) // This is a checked exception declared in a service method
throw exception;
}
}
return super.recreateRemoteInvocationResult(result);
}
}

View File

@ -5,7 +5,7 @@
*/
package com.haulmont.cuba.security.global;
import com.haulmont.cuba.core.sys.ClassesInfo;
import com.haulmont.cuba.core.global.SupportedByClient;
import java.util.UUID;
@ -16,14 +16,11 @@ import java.util.UUID;
*
* @author krivopustov
*/
@SupportedByClient
public class NoUserSessionException extends RuntimeException
{
private static final long serialVersionUID = 4820628023682230319L;
static {
ClassesInfo.addClientSupported(NoUserSessionException.class);
}
public NoUserSessionException(UUID sessionId) {
super(String.format("User session not found: %s", sessionId.toString()));
}

View File

@ -6,6 +6,7 @@
package com.haulmont.cuba.web.sys.remoting;
import com.haulmont.cuba.core.global.RemoteException;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.Deserializer;
import com.haulmont.cuba.core.sys.remoting.LocalServiceDirectory;
@ -17,9 +18,6 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.support.RemoteAccessor;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@ -116,6 +114,11 @@ public class LocalServiceProxy extends RemoteAccessor implements FactoryBean<Obj
// don't use SerializationUtils.deserialize() here to avoid ClassNotFoundException
if (result.getException() != null) {
Throwable t = (Throwable) Deserializer.deserialize(result.getException());
if (t instanceof RemoteException) {
Exception exception = ((RemoteException) t).getFirstCheckedException();
if (exception != null) // This is a checked exception declared in a service method
throw exception;
}
throw t;
} else {
Object data = Deserializer.deserialize(result.getData());