Vojtech Szocs has uploaded a new change for review.
Change subject: webadmin,userportal: Persistent client-side logging
infrastructure
......................................................................
webadmin,userportal: Persistent client-side logging infrastructure
This patch introduces following client-side features:
1, new log handler (LocalStorageLogHandler) that uses ClientStorage
for persisting log records in browser via HTML5 Local Storage API
2, global UncaughtExceptionHandler that logs any uncaught exception
(processed by all registered log handlers) and prevents it from
escaping into the browser (this means it won't appear in browser
JS error console anymore, but still will be logged - see below)
Developers Note :: we already have some useful log handlers
^^^^^^^^^^^^^^^ to aid with inspecting uncaught exceptions:
a, [ConsoleLogHandler] inspect JS console, i.e. window.console
- works in both Dev & Production mode
b, [FirebugLogHandler] inspect JS console contributed by Firebug
- works in both Dev & Production mode
c, [DevelopmentModeLogHandler / SystemLogHandler] inspect GWT
Dev Mode debug session logs / stdout of Dev Mode process
- works only in Dev mode
d, [NEW] [LocalStorageLogHandler] inspect Local Storage content
for given application, logs have keys Log_0, Log_1 etc.
LocalStorageLogHandler behavior overview:
* handles log records with INFO level and above,
log message includes exception stacktrace (if any)
* when GWT application window doesn't have focus (i.e. when
switching to another browser tab or another OS program),
log handler isn't active
Note: this is to avoid clashes in case of multiple application
instances running in browser at the same time
* uses add-only ring (circular) buffer to store log records,
in order to avoid re-arranging records in Local Storage
Example: buffer capacity = 3
Add A into buffer :: | A | | | :: head = 0, size = 1
Add B,C into buffer :: | A | B | C | :: head = 0, size = 3
Add D into buffer :: | D | B | C | :: head = 1, size = 3
List current elements in buffer -> [B, C, D]
Above buffer state maps to following Local Storage items:
LogHead = '1'
LogSize = '3'
Log_0 = 'D'
Log_1 = 'B'
Log_2 = 'C'
* log handler's ring buffer capacity is set to 100,
see LocalStorageLogHandler.LOG_CAPACITY
* to retrieve current client-side log records,
just @Inject ClientLogProvider interface
Note: in future, we'll do this to visualize client-side
logs; for now this interface isn't used
Change-Id: I3b0be449ab425b56a1d7c39efeb1793991e58fa7
Bug-Url: https://bugzilla.redhat.com/1049409
Signed-off-by: Vojtech Szocs <[email protected]>
---
M
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/GwtCommon.gwt.xml
M
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
A
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/ClientLogProvider.java
A
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/LocalStorageLogHandler.java
M
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/BaseApplicationInit.java
M
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/ClientStorage.java
A
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBuffer.java
A
frontend/webadmin/modules/gwt-common/src/test/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBufferTest.java
M
frontend/webadmin/modules/userportal-gwtp/src/main/java/org/ovirt/engine/ui/userportal/system/ApplicationInit.java
M
frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/userportal/UserPortal.gwt.xml
M
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/ApplicationInit.java
M
frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/webadmin/WebAdmin.gwt.xml
12 files changed, 495 insertions(+), 36 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/44/25444/1
diff --git
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/GwtCommon.gwt.xml
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/GwtCommon.gwt.xml
index ed1bf00..49aaec1 100644
---
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/GwtCommon.gwt.xml
+++
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/GwtCommon.gwt.xml
@@ -15,6 +15,13 @@
<inherits name="org.ovirt.engine.ui.UICommonWeb" />
<inherits name="com.google.gwt.rpc.RPC" />
+ <!-- Inherit GWT logging module and configure default handlers -->
+ <inherits name="com.google.gwt.logging.Logging" />
+ <set-property name="gwt.logging.enabled" value="TRUE" />
+ <set-property name="gwt.logging.logLevel" value="INFO" />
+ <set-property name="gwt.logging.hasWidgetsHandler" value="DISABLED" />
+ <set-property name="gwt.logging.popupHandler" value="DISABLED" />
+
<!-- All source code within this module is translatable -->
<source path="">
<!-- Except deferred binding classes -->
diff --git
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
index a37a081..a740f7b 100644
---
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
+++
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/BaseSystemModule.java
@@ -6,6 +6,8 @@
import org.ovirt.engine.ui.common.CommonApplicationTemplates;
import org.ovirt.engine.ui.common.auth.CurrentUser;
import org.ovirt.engine.ui.common.auth.LoggedInGatekeeper;
+import org.ovirt.engine.ui.common.logging.ClientLogProvider;
+import org.ovirt.engine.ui.common.logging.LocalStorageLogHandler;
import org.ovirt.engine.ui.common.system.ApplicationFocusManager;
import org.ovirt.engine.ui.common.system.AsyncCallFailureHandler;
import org.ovirt.engine.ui.common.system.ClientStorage;
@@ -50,6 +52,7 @@
bind(ClientStorage.class).in(Singleton.class);
bind(ApplicationFocusManager.class).asEagerSingleton();
bind(LockInteractionManager.class).asEagerSingleton();
+ bindTypeAndImplAsSingleton(ClientLogProvider.class,
LocalStorageLogHandler.class);
}
private void bindEventBus() {
diff --git
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/ClientLogProvider.java
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/ClientLogProvider.java
new file mode 100644
index 0000000..d68b8a4
--- /dev/null
+++
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/ClientLogProvider.java
@@ -0,0 +1,12 @@
+package org.ovirt.engine.ui.common.logging;
+
+import java.util.List;
+
+/**
+ * Classes that implement this interface provide access to client-side log
records.
+ */
+public interface ClientLogProvider {
+
+ List<String> getLogRecords();
+
+}
diff --git
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/LocalStorageLogHandler.java
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/LocalStorageLogHandler.java
new file mode 100644
index 0000000..c67fd01
--- /dev/null
+++
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/logging/LocalStorageLogHandler.java
@@ -0,0 +1,105 @@
+package org.ovirt.engine.ui.common.logging;
+
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import org.ovirt.engine.ui.common.system.ClientStorage;
+import org.ovirt.engine.ui.common.utils.AddOnlyRingBuffer;
+import org.ovirt.engine.ui.common.utils.AddOnlyRingBuffer.LinearBuffer;
+
+import com.google.gwt.logging.client.TextLogFormatter;
+import com.google.inject.Inject;
+
+/**
+ * Log handler that uses {@link ClientStorage} for persisting log records.
+ */
+public class LocalStorageLogHandler extends Handler implements
LinearBuffer<String>, ClientLogProvider {
+
+ private static final String LOG_PREFIX_KEY = "Log_"; //$NON-NLS-1$
+ private static final String LOG_HEAD_KEY = "LogHead"; //$NON-NLS-1$
+ private static final String LOG_SIZE_KEY = "LogSize"; //$NON-NLS-1$
+
+ // Maximum number of log records to keep in underlying storage
+ private static final int LOG_CAPACITY = 100;
+
+ private final ClientStorage clientStorage;
+ private final AddOnlyRingBuffer<String> logBuffer;
+
+ private boolean active = false;
+
+ @Inject
+ public LocalStorageLogHandler(ClientStorage clientStorage) {
+ this.clientStorage = clientStorage;
+ this.logBuffer = new AddOnlyRingBuffer<String>(LOG_CAPACITY, this);
+ }
+
+ public void init(Level logLevel) {
+ setFormatter(new TextLogFormatter(true));
+ setLevel(logLevel);
+ setActive(true);
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+
+ if (active) {
+ // Read logBuffer internal state
+ int newHead = readInt(LOG_HEAD_KEY, 0);
+ int newSize = readInt(LOG_SIZE_KEY, 0);
+ logBuffer.reset(newHead, newSize);
+ }
+ }
+
+ int readInt(String key, int defaultValue) {
+ try {
+ return Integer.parseInt(clientStorage.getLocalItem(key));
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ void writeInt(String key, int value) {
+ clientStorage.setLocalItem(key, String.valueOf(value));
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ if (active && isLoggable(record)) {
+ // Add message into logBuffer
+ String message = getFormatter().format(record);
+ logBuffer.add(message);
+
+ // Write logBuffer internal state
+ writeInt(LOG_HEAD_KEY, logBuffer.head());
+ writeInt(LOG_SIZE_KEY, logBuffer.size());
+ }
+ }
+
+ @Override
+ public void flush() {
+ // No action needed
+ }
+
+ @Override
+ public void close() {
+ // No action needed
+ }
+
+ @Override
+ public void write(int index, String element) {
+ clientStorage.setLocalItem(LOG_PREFIX_KEY + index, element);
+ }
+
+ @Override
+ public String read(int index) {
+ return clientStorage.getLocalItem(LOG_PREFIX_KEY + index);
+ }
+
+ @Override
+ public List<String> getLogRecords() {
+ return logBuffer.list();
+ }
+
+}
diff --git
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/BaseApplicationInit.java
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/BaseApplicationInit.java
index e3e71bc..dc7e16d 100644
---
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/BaseApplicationInit.java
+++
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/BaseApplicationInit.java
@@ -1,9 +1,14 @@
package org.ovirt.engine.ui.common.system;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
import org.ovirt.engine.core.common.businessentities.DbUser;
import org.ovirt.engine.ui.common.auth.AutoLoginData;
import org.ovirt.engine.ui.common.auth.CurrentUser;
import org.ovirt.engine.ui.common.auth.CurrentUser.LogoutHandler;
+import org.ovirt.engine.ui.common.logging.LocalStorageLogHandler;
+import
org.ovirt.engine.ui.common.system.ApplicationFocusChangeEvent.ApplicationFocusChangeHandler;
import org.ovirt.engine.ui.common.uicommon.FrontendEventsHandlerImpl;
import org.ovirt.engine.ui.common.uicommon.FrontendFailureEventListener;
import org.ovirt.engine.ui.common.uicommon.model.CleanupModelEvent;
@@ -17,6 +22,8 @@
import org.ovirt.engine.ui.uicompat.EventArgs;
import org.ovirt.engine.ui.uicompat.IEventListener;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.shared.EventBus;
@@ -37,12 +44,13 @@
protected final CurrentUser user;
protected final EventBus eventBus;
- protected final Frontend frontend;
// Using Provider because any UiCommon model will fail before TypeResolver
is initialized
private final Provider<T> loginModelProvider;
private final LockInteractionManager lockInteractionManager;
+ protected final Frontend frontend;
+ private final LocalStorageLogHandler localStorageLogHandler;
public BaseApplicationInit(ITypeResolver typeResolver,
FrontendEventsHandlerImpl frontendEventsHandler,
@@ -50,7 +58,8 @@
CurrentUser user, EventBus eventBus,
Provider<T> loginModelProvider,
LockInteractionManager lockInteractionManager,
- Frontend frontend) {
+ Frontend frontend,
+ LocalStorageLogHandler localStorageLogHandler) {
this.typeResolver = typeResolver;
this.frontendEventsHandler = frontendEventsHandler;
this.frontendFailureEventListener = frontendFailureEventListener;
@@ -59,10 +68,60 @@
this.loginModelProvider = loginModelProvider;
this.lockInteractionManager = lockInteractionManager;
this.frontend = frontend;
+ this.localStorageLogHandler = localStorageLogHandler;
}
@Override
- public void onBootstrap() {
+ public final void onBootstrap() {
+ Logger rootLogger = Logger.getLogger(""); //$NON-NLS-1$
+ initLocalStorageLogHandler(rootLogger);
+ initUncaughtExceptionHandler(rootLogger);
+
+ // Perform actual bootstrap via deferred command to ensure our
+ // UncaughtExceptionHandler is effective during the bootstrap
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ performBootstrap();
+ }
+ });
+ }
+
+ void initLocalStorageLogHandler(Logger rootLogger) {
+ // Configure and attach LocalStorageLogHandler to root logger
+ localStorageLogHandler.init(Level.INFO);
+ rootLogger.addHandler(localStorageLogHandler);
+
+ // Enable/disable LocalStorageLogHandler when the application window
gains/looses its focus
+ eventBus.addHandler(ApplicationFocusChangeEvent.getType(), new
ApplicationFocusChangeHandler() {
+ @Override
+ public void onApplicationFocusChange(ApplicationFocusChangeEvent
event) {
+ localStorageLogHandler.setActive(event.isInFocus());
+ }
+ });
+ }
+
+ void initUncaughtExceptionHandler(final Logger rootLogger) {
+ GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+ @Override
+ public void onUncaughtException(Throwable e) {
+ rootLogger.log(Level.SEVERE, "Uncaught exception occured", e);
//$NON-NLS-1$
+ }
+ });
+ }
+
+ /**
+ * Actual initialization logic that bootstraps the application.
+ * <p>
+ * Subclasses must override this method to initiate GWTP place transition
via {@code PlaceManager}, for example:
+ *
+ * <pre>
+ * super.performBootstrap(); // Common initialization (mandatory)
+ * performCustomBootstrap(); // Custom initialization (optional)
+ * placeManager.revealCurrentPlace(); // Initiate place transition
+ * </pre>
+ */
+ protected void performBootstrap() {
// Handle UI logout requests
user.setLogoutHandler(this);
diff --git
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/ClientStorage.java
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/ClientStorage.java
index fe65b14..dc43351 100644
---
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/ClientStorage.java
+++
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/system/ClientStorage.java
@@ -18,6 +18,7 @@
* <li>HTML5 session storage is transient and accessible only to one browser
window/tab
* </ul>
*/
+// TODO(vszocs) we should reconsider the use of cookie fall back due to above
limitations
public class ClientStorage {
// Fifty years
@@ -32,9 +33,9 @@
*/
public String getLocalItem(String key) {
if (localStorage != null) {
- return localStorage.getItem(key);
+ return localStorage.getItem(getItemKey(key));
} else {
- return Cookies.getCookie(key);
+ return Cookies.getCookie(getItemKey(key));
}
}
@@ -43,10 +44,10 @@
*/
public void setLocalItem(String key, String value) {
if (localStorage != null) {
- localStorage.setItem(key, value);
+ localStorage.setItem(getItemKey(key), value);
} else {
// Emulate persistent storage using cookies which have predefined
expiration date
- Cookies.setCookie(key, value, new Date(new Date().getTime() +
PERSISTENT_COOKIE_EXPIRATION));
+ Cookies.setCookie(getItemKey(key), value, new Date(new
Date().getTime() + PERSISTENT_COOKIE_EXPIRATION));
}
}
@@ -55,9 +56,9 @@
*/
public void removeLocalItem(String key) {
if (localStorage != null) {
- localStorage.removeItem(key);
+ localStorage.removeItem(getItemKey(key));
} else {
- Cookies.removeCookie(key);
+ Cookies.removeCookie(getItemKey(key));
}
}
@@ -67,9 +68,9 @@
*/
public String getSessionItem(String key) {
if (sessionStorage != null) {
- return sessionStorage.getItem(key);
+ return sessionStorage.getItem(getItemKey(key));
} else {
- return Cookies.getCookie(key);
+ return Cookies.getCookie(getItemKey(key));
}
}
@@ -78,10 +79,10 @@
*/
public void setSessionItem(String key, String value) {
if (sessionStorage != null) {
- sessionStorage.setItem(key, value);
+ sessionStorage.setItem(getItemKey(key), value);
} else {
// Emulate transient storage using cookies which expire when the
browser session ends
- Cookies.setCookie(key, value);
+ Cookies.setCookie(getItemKey(key), value);
}
}
@@ -90,10 +91,16 @@
*/
public void removeSessionItem(String key) {
if (sessionStorage != null) {
- sessionStorage.removeItem(key);
+ sessionStorage.removeItem(getItemKey(key));
} else {
- Cookies.removeCookie(key);
+ Cookies.removeCookie(getItemKey(key));
}
}
+ String getItemKey(String key) {
+ // TODO(vszocs) make key sensitive to Engine build/version number
+ // to avoid collisions in case of multiple application instances
+ return key;
+ }
+
}
diff --git
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBuffer.java
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBuffer.java
new file mode 100644
index 0000000..ee9f860
--- /dev/null
+++
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBuffer.java
@@ -0,0 +1,134 @@
+package org.ovirt.engine.ui.common.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Simple ring buffer implementation that permits only adding new elements.
+ * <p>
+ * A ring buffer is <em>First In, First Out</em> (FIFO) data structure with
fixed capacity. When adding new element into
+ * a full buffer, the oldest element is replaced. Elements are removed in the
same order in which they were added, there
+ * is no need to re-arrange elements within the underlying linear buffer
representation.
+ *
+ * @param <T>
+ * Type of element managed by the buffer.
+ */
+public class AddOnlyRingBuffer<T> {
+
+ /**
+ * Delegate interface representing a linear buffer to store the actual
data.
+ *
+ * @param <T>
+ * Type of element managed by the buffer.
+ */
+ public interface LinearBuffer<T> {
+
+ void write(int index, T element);
+
+ T read(int index);
+
+ }
+
+ private final int capacity;
+ private final LinearBuffer<T> delegate;
+
+ private int head = 0;
+ private int size = 0;
+
+ public AddOnlyRingBuffer(int capacity, LinearBuffer<T> delegate) {
+ if (delegate == null) {
+ throw new NullPointerException("delegate cannot be null");
//$NON-NLS-1$
+ } else if (capacity < 1) {
+ throw new IllegalArgumentException("capacity must be positive");
//$NON-NLS-1$
+ }
+ this.capacity = capacity;
+ this.delegate = delegate;
+ }
+
+ /**
+ * Adds the given element to the ring buffer.
+ * <p>
+ * Performs in constant time O(1).
+ *
+ * @return Oldest element removed due to buffer being full, {@code null}
otherwise.
+ */
+ public T add(T element) {
+ T old = null;
+
+ // Buffer not full yet, don't move the head
+ if (size < capacity) {
+ int index = head + size;
+ size += 1;
+ delegate.write(index, element);
+ }
+ // Buffer is full, need to move the head
+ else {
+ int index = head;
+ head = (head + 1) % capacity;
+ old = delegate.read(index);
+ delegate.write(index, element);
+ }
+
+ return old;
+ }
+
+ /**
+ * Returns the list of elements present in the ring buffer.
+ * <p>
+ * Performs in linear time O(n).
+ */
+ public List<T> list() {
+ List<T> result = new ArrayList<T>(size);
+ for (int offset = 0; offset < size; offset += 1) {
+ int index = (head + offset) % capacity;
+ result.add(delegate.read(index));
+ }
+ return result;
+ }
+
+ /**
+ * Returns the head index pointing to oldest element in the ring buffer.
+ */
+ public int head() {
+ return head;
+ }
+
+ /**
+ * Returns the number of elements present in the ring buffer.
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns {@code true} if the ring buffer size is zero.
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * Returns {@code true} if the ring buffer reached its capacity.
+ */
+ public boolean isFull() {
+ return size() == capacity;
+ }
+
+ /**
+ * Resets the internal state of the ring buffer.
+ */
+ public void reset(int newHead, int newSize) {
+ // 0 <= newHead < capacity
+ if (newHead < 0 || newHead >= capacity) {
+ throw new IllegalArgumentException("newHead out of bounds");
//$NON-NLS-1$
+ }
+ // 0 <= newSize <= capacity
+ else if (newSize < 0 || newSize > capacity) {
+ throw new IllegalArgumentException("newSize out of bounds");
//$NON-NLS-1$
+ }
+
+ this.head = newHead;
+ this.size = newSize;
+ }
+
+}
diff --git
a/frontend/webadmin/modules/gwt-common/src/test/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBufferTest.java
b/frontend/webadmin/modules/gwt-common/src/test/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBufferTest.java
new file mode 100644
index 0000000..dd3b9dd
--- /dev/null
+++
b/frontend/webadmin/modules/gwt-common/src/test/java/org/ovirt/engine/ui/common/utils/AddOnlyRingBufferTest.java
@@ -0,0 +1,141 @@
+package org.ovirt.engine.ui.common.utils;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.ovirt.engine.ui.common.utils.AddOnlyRingBuffer.LinearBuffer;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AddOnlyRingBufferTest {
+
+ private class StringArrayBuffer implements LinearBuffer<String> {
+
+ private final String[] array = new String[CAPACITY];
+
+ @Override
+ public void write(int index, String element) {
+ array[index] = element;
+ }
+
+ @Override
+ public String read(int index) {
+ return array[index];
+ }
+
+ }
+
+ private static final int CAPACITY = 5;
+
+ private AddOnlyRingBuffer<String> tested;
+
+ @Before
+ public void setUp() {
+ tested = new AddOnlyRingBuffer<String>(CAPACITY, new
StringArrayBuffer());
+ }
+
+ String[] testedListToArray() {
+ return tested.list().toArray(new String[0]);
+ }
+
+ @Test
+ public void bufferEmpty() {
+ assertArrayEquals(testedListToArray(), new String[0]);
+ assertEquals(tested.head(), 0);
+ assertEquals(tested.size(), 0);
+ assertTrue(tested.isEmpty());
+ assertFalse(tested.isFull());
+ }
+
+ @Test
+ public void bufferBelowCapacity() {
+ assertNull(tested.add("A")); //$NON-NLS-1$
+ assertNull(tested.add("B")); //$NON-NLS-1$
+ assertArrayEquals(testedListToArray(), new String[] { "A", "B" });
//$NON-NLS-1$ //$NON-NLS-2$
+ assertEquals(tested.head(), 0);
+ assertEquals(tested.size(), 2);
+ assertFalse(tested.isEmpty());
+ assertFalse(tested.isFull());
+ }
+
+ @Test
+ public void bufferAtCapacity() {
+ assertNull(tested.add("A")); //$NON-NLS-1$
+ assertNull(tested.add("B")); //$NON-NLS-1$
+ assertNull(tested.add("C")); //$NON-NLS-1$
+ assertNull(tested.add("D")); //$NON-NLS-1$
+ assertNull(tested.add("E")); //$NON-NLS-1$
+ assertArrayEquals(testedListToArray(), new String[] { "A", "B", "C",
"D", "E" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
//$NON-NLS-5$
+ assertEquals(tested.head(), 0);
+ assertEquals(tested.size(), 5);
+ assertFalse(tested.isEmpty());
+ assertTrue(tested.isFull());
+ }
+
+ @Test
+ public void bufferBeyondCapacity() {
+ assertNull(tested.add("A")); //$NON-NLS-1$
+ assertNull(tested.add("B")); //$NON-NLS-1$
+ assertNull(tested.add("C")); //$NON-NLS-1$
+ assertNull(tested.add("D")); //$NON-NLS-1$
+ assertNull(tested.add("E")); //$NON-NLS-1$
+ assertEquals("A", tested.add("F")); //$NON-NLS-1$ //$NON-NLS-2$
+ assertEquals("B", tested.add("G")); //$NON-NLS-1$ //$NON-NLS-2$
+ assertArrayEquals(testedListToArray(), new String[] { "C", "D", "E",
"F", "G" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
//$NON-NLS-5$
+ assertEquals(tested.head(), 2);
+ assertEquals(tested.size(), 5);
+ assertFalse(tested.isEmpty());
+ assertTrue(tested.isFull());
+ }
+
+ @Test
+ public void bufferReset() {
+ assertNull(tested.add("A")); //$NON-NLS-1$
+ assertNull(tested.add("B")); //$NON-NLS-1$
+ assertNull(tested.add("C")); //$NON-NLS-1$
+ assertNull(tested.add("D")); //$NON-NLS-1$
+ tested.reset(1, 2);
+ assertArrayEquals(testedListToArray(), new String[] { "B", "C" });
//$NON-NLS-1$ //$NON-NLS-2$
+ assertEquals(tested.head(), 1);
+ assertEquals(tested.size(), 2);
+ assertFalse(tested.isEmpty());
+ assertFalse(tested.isFull());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void bufferReset_headLowerBound() {
+ tested.reset(-1, 2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void bufferReset_headUpperBound() {
+ tested.reset(CAPACITY, 2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void bufferReset_sizeLowerBound() {
+ tested.reset(1, -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void bufferReset_sizeUpperBound() {
+ tested.reset(1, CAPACITY + 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructorInvariants_capacityLowerBound() {
+ new AddOnlyRingBuffer<String>(0, new StringArrayBuffer());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void constructorInvariants_delegateNull() {
+ new AddOnlyRingBuffer<String>(CAPACITY, null);
+ }
+
+}
diff --git
a/frontend/webadmin/modules/userportal-gwtp/src/main/java/org/ovirt/engine/ui/userportal/system/ApplicationInit.java
b/frontend/webadmin/modules/userportal-gwtp/src/main/java/org/ovirt/engine/ui/userportal/system/ApplicationInit.java
index a9bdb92..5652f9c 100644
---
a/frontend/webadmin/modules/userportal-gwtp/src/main/java/org/ovirt/engine/ui/userportal/system/ApplicationInit.java
+++
b/frontend/webadmin/modules/userportal-gwtp/src/main/java/org/ovirt/engine/ui/userportal/system/ApplicationInit.java
@@ -1,6 +1,7 @@
package org.ovirt.engine.ui.userportal.system;
import org.ovirt.engine.ui.common.auth.CurrentUser;
+import org.ovirt.engine.ui.common.logging.LocalStorageLogHandler;
import org.ovirt.engine.ui.common.system.BaseApplicationInit;
import org.ovirt.engine.ui.common.system.LockInteractionManager;
import org.ovirt.engine.ui.common.uicommon.ClientAgentType;
@@ -42,13 +43,14 @@
Provider<UserPortalLoginModel> loginModelProvider,
LockInteractionManager lockInteractionManager,
Frontend frontend,
+ LocalStorageLogHandler localStorageLogHandler,
PlaceManager placeManager,
CurrentUserRole userRole,
ConnectAutomaticallyManager connectAutomaticallyManager,
ClientAgentType clientAgentType,
ApplicationDynamicMessages dynamicMessages) {
- super(typeResolver, frontendEventsHandler,
frontendFailureEventListener,
- user, eventBus, loginModelProvider, lockInteractionManager,
frontend);
+ super(typeResolver, frontendEventsHandler,
frontendFailureEventListener, user, eventBus,
+ loginModelProvider, lockInteractionManager, frontend,
localStorageLogHandler);
this.placeManager = placeManager;
this.userRole = userRole;
this.connectAutomaticallyManager = connectAutomaticallyManager;
@@ -57,8 +59,8 @@
}
@Override
- public void onBootstrap() {
- super.onBootstrap();
+ protected void performBootstrap() {
+ super.performBootstrap();
Window.setTitle(dynamicMessages.applicationTitle());
// Initiate transition to requested application place
diff --git
a/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/userportal/UserPortal.gwt.xml
b/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/userportal/UserPortal.gwt.xml
index f13a67b..8f1d05d 100644
---
a/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/userportal/UserPortal.gwt.xml
+++
b/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/userportal/UserPortal.gwt.xml
@@ -9,12 +9,6 @@
<!-- Inherit the default GWT theme -->
<inherits name="com.google.gwt.user.theme.standard.Standard" />
- <!-- Inherit GWT logging module and configure it -->
- <inherits name="com.google.gwt.logging.Logging" />
- <set-property name="gwt.logging.enabled" value="TRUE" />
- <set-property name="gwt.logging.logLevel" value="INFO" />
- <set-property name="gwt.logging.popupHandler" value="DISABLED" />
-
<!-- Supported browsers (defined via maven property) -->
<set-property name="user.agent" value="${gwt.userAgent}" />
diff --git
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/ApplicationInit.java
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/ApplicationInit.java
index 882e053..a4f7c82 100644
---
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/ApplicationInit.java
+++
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/ApplicationInit.java
@@ -3,6 +3,7 @@
import org.ovirt.engine.core.common.mode.ApplicationMode;
import org.ovirt.engine.ui.common.auth.AutoLoginData;
import org.ovirt.engine.ui.common.auth.CurrentUser;
+import org.ovirt.engine.ui.common.logging.LocalStorageLogHandler;
import org.ovirt.engine.ui.common.system.BaseApplicationInit;
import org.ovirt.engine.ui.common.system.LockInteractionManager;
import org.ovirt.engine.ui.common.uicommon.FrontendEventsHandlerImpl;
@@ -45,20 +46,20 @@
Provider<LoginModel> loginModelProvider,
LockInteractionManager lockInteractionManager,
Frontend frontend,
+ LocalStorageLogHandler localStorageLogHandler,
PlaceManager placeManager,
RestApiSessionManager restApiSessionManager,
ApplicationDynamicMessages dynamicMessages) {
- super(typeResolver, frontendEventsHandler,
frontendFailureEventListener,
- user, eventBus, loginModelProvider, lockInteractionManager,
frontend);
+ super(typeResolver, frontendEventsHandler,
frontendFailureEventListener, user, eventBus,
+ loginModelProvider, lockInteractionManager, frontend,
localStorageLogHandler);
this.placeManager = placeManager;
this.restApiSessionManager = restApiSessionManager;
this.dynamicMessages = dynamicMessages;
-
}
@Override
- public void onBootstrap() {
- super.onBootstrap();
+ protected void performBootstrap() {
+ super.performBootstrap();
Window.setTitle(dynamicMessages.applicationTitle());
// Check for ApplicationMode configuration
diff --git
a/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/webadmin/WebAdmin.gwt.xml
b/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/webadmin/WebAdmin.gwt.xml
index c93556f..a2394a5 100644
---
a/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/webadmin/WebAdmin.gwt.xml
+++
b/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/webadmin/WebAdmin.gwt.xml
@@ -9,12 +9,6 @@
<!-- Inherit the default GWT theme -->
<inherits name="com.google.gwt.user.theme.standard.Standard" />
- <!-- Inherit GWT logging module and configure it -->
- <inherits name="com.google.gwt.logging.Logging" />
- <set-property name="gwt.logging.enabled" value="TRUE" />
- <set-property name="gwt.logging.logLevel" value="INFO" />
- <set-property name="gwt.logging.popupHandler" value="DISABLED" />
-
<!-- Supported browsers (defined via maven property) -->
<set-property name="user.agent" value="${gwt.userAgent}" />
--
To view, visit http://gerrit.ovirt.org/25444
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I3b0be449ab425b56a1d7c39efeb1793991e58fa7
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Vojtech Szocs <[email protected]>
_______________________________________________
Engine-patches mailing list
[email protected]
http://lists.ovirt.org/mailman/listinfo/engine-patches