Vojtech Szocs has uploaded a new change for review. Change subject: webadmin: Fix UI plugin REST API / Engine session refresh issue ......................................................................
webadmin: Fix UI plugin REST API / Engine session refresh issue Patch http://gerrit.ovirt.org/#/c/35185/ ensured that UI plugin REST API session uses the existing Engine user session, instead of creating a separate one. This caused REST API session heartbeat mechanism to eventually keep the Engine user session alive, effectively preventing GUI from auto-logout due to GUI user's inactivity. This patch syncs REST API session with Engine user session: upon each backend call that refreshes Engine session (action or query with refresh=true), REST API session is refreshed. REST API session refresh is done max. once per 1 minute to prevent flooding the backend. Change-Id: I2ffd3198239040f67313238a837af98db16e79ea Bug-Url: https://bugzilla.redhat.com/1172726 Bug-Url: https://bugzilla.redhat.com/1168842 Signed-off-by: Vojtech Szocs <vsz...@redhat.com> --- M frontend/webadmin/modules/frontend/exclude-filters.xml A frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/EngineSessionRefreshed.java M frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java M frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java M frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java M frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManagerTest.java M frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java 7 files changed, 89 insertions(+), 12 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/10/36710/1 diff --git a/frontend/webadmin/modules/frontend/exclude-filters.xml b/frontend/webadmin/modules/frontend/exclude-filters.xml index ae17cae..3b9ca50 100644 --- a/frontend/webadmin/modules/frontend/exclude-filters.xml +++ b/frontend/webadmin/modules/frontend/exclude-filters.xml @@ -38,6 +38,18 @@ <Bug code="UuF"/> </Match> + <!-- + findbugs complains about unused field(s) in this class. + This is a GWT class and used to generate code. + + findbugs reason: + UuF: Unused field (UUF_UNUSED_FIELD) + --> + <Match> + <Class name="org.ovirt.engine.ui.frontend.communication.EngineSessionRefreshed" /> + <Bug code="UuF"/> + </Match> + <Match> <Class name="org.ovirt.engine.ui.frontend.AsyncQuery" /> <Field name="converterCallback"/> diff --git a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/EngineSessionRefreshed.java b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/EngineSessionRefreshed.java new file mode 100644 index 0000000..8f1c57c --- /dev/null +++ b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/EngineSessionRefreshed.java @@ -0,0 +1,11 @@ +package org.ovirt.engine.ui.frontend.communication; + +import com.gwtplatform.dispatch.annotation.GenEvent; + +/** + * Event triggered when the Engine session is refreshed, i.e. due to executing backend action or query. + */ +@GenEvent +public class EngineSessionRefreshed { + +} diff --git a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java index 2c00dd2..58b9e61 100644 --- a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java +++ b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java @@ -6,13 +6,20 @@ import org.ovirt.engine.core.common.action.LoginUserParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; +import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; +import com.google.gwt.event.shared.EventBus; import com.google.inject.Inject; /** * This class is a singleton and manages how {@code VdcOperation}s are added to the queue to be processed. */ public class VdcOperationManager { + + /** + * Event bus to propagate events related to operation processing. + */ + private final EventBus eventBus; /** * The operation queue. It can hold any kind of VdcOperation. @@ -34,7 +41,8 @@ * @param operationProcessor the operation processor. */ @Inject - public VdcOperationManager(final OperationProcessor operationProcessor) { + public VdcOperationManager(final EventBus eventBus, final OperationProcessor operationProcessor) { + this.eventBus = eventBus; this.processor = operationProcessor; } @@ -73,6 +81,10 @@ if (isAllowedToExecute) { if (operationCanBeAdded && operationQueue.add(operation)) { processor.processOperation(this); + + if (engineSessionRefreshed(operation)) { + EngineSessionRefreshedEvent.fire(eventBus); + } } } @@ -169,4 +181,22 @@ public void retrieveFromHttpSession(final String key, final StorageCallback callback) { processor.retrieveFromHttpSession(key, callback); } + + /** + * Returns {@code true} if execution of given operation caused the Engine session to be refreshed. + */ + boolean engineSessionRefreshed(VdcOperation<?, ?> operation) { + // Actions always refresh the Engine session + if (operation.isAction()) { + return true; + } + + // Queries optionally refresh the Engine session + else if (((VdcQueryParametersBase) operation.getParameter()).getRefresh()) { + return true; + } + + return false; + } + } diff --git a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java index b3256dc..97cf835 100644 --- a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java +++ b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendActionTest.java @@ -116,7 +116,7 @@ when(mockXsrfRpcRequestBuilder.getXsrfToken()).thenReturn(new XsrfToken("Something")); //$NON-NLS-1$ OperationProcessor operationProcessor = new OperationProcessor(communicationsProvider); operationProcessor.setScheduler(fakeScheduler); - VdcOperationManager operationsManager = new VdcOperationManager(operationProcessor); + VdcOperationManager operationsManager = new VdcOperationManager(mockEventBus, operationProcessor); operationsManager.setLoggedIn(true); frontend = new Frontend(operationsManager, mockCanDoActionErrorsTranslator, mockVdsmErrorsTranslator, mockEventBus); diff --git a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java index 189c228..5d7ad90 100644 --- a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java +++ b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/FrontendTest.java @@ -109,7 +109,7 @@ when(mockXsrfRpcRequestBuilder.getXsrfToken()).thenReturn(new XsrfToken("Something")); //$NON-NLS-1$ OperationProcessor operationProcessor = new OperationProcessor(communicationsProvider); operationProcessor.setScheduler(fakeScheduler); - VdcOperationManager operationsManager = new VdcOperationManager(operationProcessor); + VdcOperationManager operationsManager = new VdcOperationManager(mockEventBus, operationProcessor); operationsManager.setLoggedIn(true); frontend = new Frontend(operationsManager, mockCanDoActionErrorsTranslator, mockVdsmErrorsTranslator, mockEventBus); diff --git a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManagerTest.java b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManagerTest.java index 6fa0b92..ef0839b 100644 --- a/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManagerTest.java +++ b/frontend/webadmin/modules/frontend/src/test/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManagerTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -23,12 +24,18 @@ import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryType; +import com.google.gwt.event.shared.EventBus; + @RunWith(MockitoJUnitRunner.class) public class VdcOperationManagerTest { VdcOperationManager testManager; @Mock OperationProcessor mockOperationProcessor; + + @Mock + EventBus mockEventBus; + @Captor ArgumentCaptor<VdcOperation<VdcActionType, LoginUserParameters>> loginOperationCaptor; @@ -36,7 +43,7 @@ @Before public void setUp() throws Exception { final VdcReturnValueBase result = new VdcReturnValueBase(); - testManager = new VdcOperationManager(mockOperationProcessor); + testManager = new VdcOperationManager(mockEventBus, mockOperationProcessor); final VdcReturnValueBase loginResult = new VdcReturnValueBase(); LoginUserParameters params = new LoginUserParameters("test", "test", //$NON-NLS-1$ //$NON-NLS-2$ "test"); //$NON-NLS-1$ @@ -70,6 +77,7 @@ new VdcActionParametersBase(), null); testManager.addOperation(testOperation); verify(mockOperationProcessor).processOperation(testManager); + verify(mockEventBus).fireEvent(any(EngineSessionRefreshedEvent.class)); assertEquals("Operations must match", testOperation, testManager.pollOperation()); //$NON-NLS-1$ } @@ -79,10 +87,12 @@ VdcQueryParametersBase>(VdcQueryType.Search, new VdcQueryParametersBase(), null); testManager.addOperation(testOperation); verify(mockOperationProcessor).processOperation(testManager); + verify(mockEventBus).fireEvent(any(EngineSessionRefreshedEvent.class)); // Second add, shouldn't add and generate an event. testManager.addOperation(testOperation); // Verify it is only called once (from before) verify(mockOperationProcessor).processOperation(testManager); + verify(mockEventBus).fireEvent(any(EngineSessionRefreshedEvent.class)); } @Test @@ -101,6 +111,7 @@ operationList.add(testOperation3); testManager.addOperationList(operationList); verify(mockOperationProcessor, times(3)).processOperation(testManager); + verify(mockEventBus, times(2)).fireEvent(any(EngineSessionRefreshedEvent.class)); assertEquals("First poll should be action", testManager.pollOperation(), testOperation1); //$NON-NLS-1$ assertEquals("Second poll should be query", testManager.pollOperation(), testOperation2); //$NON-NLS-1$ assertNull("Third poll should be null", testManager.pollOperation()); //$NON-NLS-1$ diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java index d6ead3f..d68fc0f 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java @@ -5,6 +5,8 @@ import org.ovirt.engine.ui.common.utils.HttpUtils; import org.ovirt.engine.ui.frontend.Frontend; +import org.ovirt.engine.ui.frontend.communication.EngineSessionRefreshedEvent; +import org.ovirt.engine.ui.frontend.communication.EngineSessionRefreshedEvent.EngineSessionRefreshedHandler; import org.ovirt.engine.ui.frontend.communication.StorageCallback; import org.ovirt.engine.ui.frontend.utils.BaseContextPathData; @@ -38,7 +40,7 @@ * <p> * Triggers {@link RestApiSessionAcquiredEvent} upon acquiring or reusing REST API session. */ -public class RestApiSessionManager { +public class RestApiSessionManager implements EngineSessionRefreshedHandler { private static class RestApiRequestCallback implements RequestCallback { @@ -81,6 +83,8 @@ private String restApiSessionTimeout = DEFAULT_SESSION_TIMEOUT; private String restApiSessionId; + private boolean refreshRestApiSession = false; + @Inject public RestApiSessionManager(EventBus eventBus) { this.eventBus = eventBus; @@ -89,6 +93,15 @@ // send authentication headers to URLs ending in api/, otherwise it will send them to URLs ending in /, and // this causes problems in other applications, for example in the reports application. this.restApiBaseUrl = BaseContextPathData.getInstance().getPath() + "api/"; //$NON-NLS-1$ + + eventBus.addHandler(EngineSessionRefreshedEvent.getType(), this); + } + + @Override + public void onEngineSessionRefreshed(EngineSessionRefreshedEvent event) { + if (restApiSessionId != null) { + refreshRestApiSession = true; + } } public void setSessionTimeout(String sessionTimeout) { @@ -140,18 +153,18 @@ Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { @Override public boolean execute() { - String sessionId = getSessionId(); + boolean sessionInUse = restApiSessionId != null; - if (sessionId != null) { + if (sessionInUse && refreshRestApiSession) { // The browser takes care of sending JSESSIONID cookie for this request automatically sendRequest(createRequest(null), new RestApiRequestCallback()); - // The session is still in use, proceed with the heartbeat - return true; - } else { - // The session has been released, cancel the heartbeat - return false; + // Reset the refresh flag + refreshRestApiSession = false; } + + // Proceed with the heartbeat only when the session is still in use + return sessionInUse; } }, SESSION_HEARTBEAT_MS); } -- To view, visit http://gerrit.ovirt.org/36710 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I2ffd3198239040f67313238a837af98db16e79ea Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: ovirt-engine-3.5 Gerrit-Owner: Vojtech Szocs <vsz...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches