Browse Source

Catch and log exceptions in session lifecycle listeners (#12915)

Change-Id: Ie8638f010d74c569c5ff56e91c95e23a5cb92c9b
tags/7.1.9
Henri Sara 10 years ago
parent
commit
10ca7ed571

+ 38
- 1
server/src/com/vaadin/event/EventRouter.java View File

@@ -23,6 +23,10 @@ import java.util.EventObject;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.logging.Logger;

import com.vaadin.server.ErrorEvent;
import com.vaadin.server.ErrorHandler;

/**
* <code>EventRouter</code> class implementing the inheritable event listening
@@ -154,6 +158,25 @@ public class EventRouter implements MethodEventSource {
* the Event to be sent to all listeners.
*/
public void fireEvent(EventObject event) {
fireEvent(event, null);
}

/**
* Sends an event to all registered listeners. The listeners will decide if
* the activation method should be called or not.
* <p>
* If an error handler is set, the processing of other listeners will
* continue after the error handler method call unless the error handler
* itself throws an exception.
*
* @param event
* the Event to be sent to all listeners.
* @param errorHandler
* error handler to use to handle any exceptions thrown by
* listeners or null to let the exception propagate to the
* caller, preventing further listener calls
*/
public void fireEvent(EventObject event, ErrorHandler errorHandler) {
// It is not necessary to send any events if there are no listeners
if (listenerList != null) {

@@ -164,7 +187,16 @@ public class EventRouter implements MethodEventSource {
// will filter out unwanted events.
final Object[] listeners = listenerList.toArray();
for (int i = 0; i < listeners.length; i++) {
((ListenerMethod) listeners[i]).receiveEvent(event);
ListenerMethod listenerMethod = (ListenerMethod) listeners[i];
if (null != errorHandler) {
try {
listenerMethod.receiveEvent(event);
} catch (Exception e) {
errorHandler.error(new ErrorEvent(e));
}
} else {
listenerMethod.receiveEvent(event);
}
}

}
@@ -208,4 +240,9 @@ public class EventRouter implements MethodEventSource {
}
return listeners;
}

private Logger getLogger() {
return Logger.getLogger(EventRouter.class.getName());
}

}

+ 13
- 2
server/src/com/vaadin/server/VaadinService.java View File

@@ -414,6 +414,9 @@ public abstract class VaadinService implements Serializable {
/**
* Adds a listener that gets notified when a Vaadin service session that has
* been initialized for this service is destroyed.
* <p>
* The session being destroyed is locked and its UIs have been removed when
* the listeners are called.
*
* @see #addSessionInitListener(SessionInitListener)
*
@@ -455,8 +458,11 @@ public abstract class VaadinService implements Serializable {
}
});
}
// for now, use the session error handler; in the future, could
// have an API for using some other handler for session init and
// destroy listeners
eventRouter.fireEvent(new SessionDestroyEvent(
VaadinService.this, session));
VaadinService.this, session), session.getErrorHandler());
}
});
}
@@ -770,7 +776,12 @@ public abstract class VaadinService implements Serializable {

private void onVaadinSessionStarted(VaadinRequest request,
VaadinSession session) throws ServiceException {
eventRouter.fireEvent(new SessionInitEvent(this, session, request));
// for now, use the session error handler; in the future, could have an
// API for using some other handler for session init and destroy
// listeners

eventRouter.fireEvent(new SessionInitEvent(this, session, request),
session.getErrorHandler());

ServletPortletHelper.checkUiProviders(session, this);
}

+ 111
- 0
server/tests/src/com/vaadin/tests/event/EventRouterTest.java View File

@@ -0,0 +1,111 @@
/*
* Copyright 2000-2013 Vaadin Ltd.
*
* 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 com.vaadin.tests.event;

import java.lang.reflect.Method;

import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.event.EventRouter;
import com.vaadin.server.ErrorEvent;
import com.vaadin.server.ErrorHandler;
import com.vaadin.ui.Component;
import com.vaadin.ui.Component.Listener;
import com.vaadin.util.ReflectTools;

/**
* Test EventRouter and related error handling.
*/
public class EventRouterTest {

private static final Method COMPONENT_EVENT_METHOD = ReflectTools
.findMethod(Component.Listener.class, "componentEvent",
Component.Event.class);

private EventRouter router;
private Component component;
private ErrorHandler errorHandler;
private Listener listener;

@Before
public void createMocks() {
router = new EventRouter();
component = EasyMock.createNiceMock(Component.class);
errorHandler = EasyMock.createMock(ErrorHandler.class);
listener = EasyMock.createMock(Component.Listener.class);
router.addListener(Component.Event.class, listener,
COMPONENT_EVENT_METHOD);
}

@Test
public void fireEvent_noException_eventReceived() {
listener.componentEvent(EasyMock.<Component.Event> anyObject());

EasyMock.replay(component, listener, errorHandler);
router.fireEvent(new Component.Event(component), errorHandler);
EasyMock.verify(listener, errorHandler);
}

@Test
public void fireEvent_exceptionFromListenerAndNoHandler_exceptionPropagated() {
listener.componentEvent(EasyMock.<Component.Event> anyObject());
EasyMock.expectLastCall().andThrow(
new RuntimeException("listener failed"));

EasyMock.replay(component, listener);
try {
router.fireEvent(new Component.Event(component));
Assert.fail("Did not receive expected exception from listener");
} catch (RuntimeException e) {
// e is a ListenerMethod@MethodException
Assert.assertEquals("listener failed", e.getCause().getMessage());
}
EasyMock.verify(listener);
}

@Test
public void fireEvent_exceptionFromListener_errorHandlerCalled() {
listener.componentEvent(EasyMock.<Component.Event> anyObject());
EasyMock.expectLastCall().andThrow(
new RuntimeException("listener failed"));
errorHandler.error(EasyMock.<ErrorEvent> anyObject());

EasyMock.replay(component, listener, errorHandler);
router.fireEvent(new Component.Event(component), errorHandler);
EasyMock.verify(listener, errorHandler);
}

@Test
public void fireEvent_multipleListenersAndException_errorHandlerCalled() {
Listener listener2 = EasyMock.createMock(Component.Listener.class);
router.addListener(Component.Event.class, listener2,
COMPONENT_EVENT_METHOD);

listener.componentEvent(EasyMock.<Component.Event> anyObject());
EasyMock.expectLastCall().andThrow(
new RuntimeException("listener failed"));
errorHandler.error(EasyMock.<ErrorEvent> anyObject());
// second listener should be called despite an error in the first
listener2.componentEvent(EasyMock.<Component.Event> anyObject());

EasyMock.replay(component, listener, listener2, errorHandler);
router.fireEvent(new Component.Event(component), errorHandler);
EasyMock.verify(listener, listener2, errorHandler);
}
}

Loading…
Cancel
Save