bug4100 Improved settings framework architecture

This commit is contained in:
Vladislav Chumak committed 2017-05-04 18:36:32 +02:00
1 parent 9f3876ba18
commit eca9002e57
31 files changed
+894 -1251

No files matched your search

@@ -30,8 +30,8 @@ import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
import com.sap.sse.gwt.client.shared.perspective.AbstractPerspectiveComposite;
import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage;
/**
* A perspective managing a header with a single leaderboard filling the rest of the screen.
@@ -14,12 +14,10 @@ import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sse.gwt.client.mutationobserver.ElementSizeMutationObserver;
import com.sap.sse.gwt.client.mutationobserver.ElementSizeMutationObserver.DomMutationCallback;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.DefaultOnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.settings.PlaceBasedUserSettingsStorageManager;
import com.sap.sse.security.ui.settings.PlaceBasedComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.settings.StorageDefinitionId;
/**
@@ -82,13 +80,11 @@ public abstract class SharedLeaderboardRegattaTabView<T extends AbstractEventReg
protected ComponentContext<LeaderboardSettings> createLeaderboardComponentContext(String leaderboardName, UserService userService,
String placeToken) {
final LeaderboardPanelLifecycle lifeCycle = new LeaderboardPanelLifecycle(null, StringMessages.INSTANCE);
final LeaderboardPanelLifecycle lifecycle = new LeaderboardPanelLifecycle(null, StringMessages.INSTANCE);
final StorageDefinitionId storageDefinitionId = StorageDefinitionIdFactory.createStorageDefinitionIdForEventRegattaLeaderboard(leaderboardName);
final SettingsStorageManager<LeaderboardSettings> settingsStorageManager = new PlaceBasedUserSettingsStorageManager<>(
userService, storageDefinitionId, placeToken);
final ComponentContext<LeaderboardSettings> componentContext = new ComponentContextWithSettingsStorage<>(
lifeCycle, settingsStorageManager);
final ComponentContext<LeaderboardSettings> componentContext = new PlaceBasedComponentContextWithSettingsStorage<>(
lifecycle, userService, storageDefinitionId, placeToken);
return componentContext;
}
}
@@ -12,12 +12,10 @@ import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.DefaultOnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.settings.PlaceBasedUserSettingsStorageManager;
import com.sap.sse.security.ui.settings.PlaceBasedComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.settings.StorageDefinitionId;
/**
@@ -64,13 +62,11 @@ public abstract class SharedLeaderboardEventSeriesTabView<T extends AbstractSeri
protected ComponentContext<LeaderboardSettings> createLeaderboardComponentContext(String leaderboardName, UserService userService,
String placeToken) {
final LeaderboardPanelLifecycle lifeCycle = new LeaderboardPanelLifecycle(null, StringMessages.INSTANCE);
final LeaderboardPanelLifecycle lifecycle = new LeaderboardPanelLifecycle(null, StringMessages.INSTANCE);
final StorageDefinitionId storageDefinitionId = StorageDefinitionIdFactory.createStorageDefinitionIdForSeriesOverallLeaderboard(leaderboardName);
final SettingsStorageManager<LeaderboardSettings> settingsStorageManager = new PlaceBasedUserSettingsStorageManager<>(
userService, storageDefinitionId, placeToken);
final ComponentContext<LeaderboardSettings> componentContext = new ComponentContextWithSettingsStorage<>(
lifeCycle, settingsStorageManager);
final ComponentContext<LeaderboardSettings> componentContext = new PlaceBasedComponentContextWithSettingsStorage<>(
lifecycle, userService, storageDefinitionId, placeToken);
return componentContext;
}
}
@@ -27,12 +27,10 @@ import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.MultiLeaderboardProxyPanel;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.DefaultOnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.settings.PlaceBasedUserSettingsStorageManager;
import com.sap.sse.security.ui.settings.PlaceBasedComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.settings.StorageDefinitionId;
public class EventSeriesLeaderboardsTabView extends Composite implements SeriesTabView<EventSeriesLeaderboardsPlace>,
@@ -130,11 +128,9 @@ public class EventSeriesLeaderboardsTabView extends Composite implements SeriesT
String placeToken) {
final MultiLeaderboardPanelLifecycle lifecycle = new MultiLeaderboardPanelLifecycle(null, StringMessages.INSTANCE);
final StorageDefinitionId storageDefinitionId = StorageDefinitionIdFactory.createStorageDefinitionIdForSeriesRegattaLeaderboards(leaderboardName);
final SettingsStorageManager<LeaderboardSettings> settingsStorageManager = new PlaceBasedUserSettingsStorageManager<>(
userService, storageDefinitionId, placeToken);
final ComponentContext<LeaderboardSettings> componentContext = new ComponentContextWithSettingsStorage<>(
lifecycle, settingsStorageManager);
final ComponentContext<LeaderboardSettings> componentContext = new PlaceBasedComponentContextWithSettingsStorage<>(
lifecycle, userService, storageDefinitionId, placeToken);
return componentContext;
}
@@ -40,13 +40,12 @@ import com.sap.sse.gwt.client.player.Timer.PlayModes;
import com.sap.sse.gwt.client.player.Timer.PlayStates;
import com.sap.sse.gwt.client.shared.components.LinkWithSettingsGenerator;
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
import com.sap.sse.gwt.client.shared.perspective.IgnoreLocalSettings;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.DefaultOnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.settings.StorageDefinitionId;
import com.sap.sse.security.ui.settings.UserSettingsStorageManager;
public class RegattaOverviewPanel extends SimplePanel {
@@ -130,7 +129,7 @@ public class RegattaOverviewPanel extends SimplePanel {
// TODO should we always set ignoreLocalSettings=true when creating links?
new SettingsDialog<RegattaRaceStatesSettings>(regattaRaceStatesComponent, stringMessages,
new LinkWithSettingsGenerator<>(regattaOverviewContextDefinition,
UserSettingsStorageManager.getIgnoreLocalSettings())).show();
IgnoreLocalSettings.getIgnoreLocalSettingsFromCurrentUrl())).show();
}
});
@@ -154,10 +153,8 @@ public class RegattaOverviewPanel extends SimplePanel {
final StorageDefinitionId storageDefinitionId = StorageDefinitionIdFactory.createStorageDefinitionIdForRegattaOverview(regattaOverviewContextDefinition);
final RegattaRaceStatesComponentLifecycle lifecycle = new RegattaRaceStatesComponentLifecycle();
final SettingsStorageManager<RegattaRaceStatesSettings> settingsStorageManager = UserSettingsStorageManager
.createSettingsStorageManager(userService, storageDefinitionId);
final ComponentContext<RegattaRaceStatesSettings> componentContext = new ComponentContextWithSettingsStorage<>(
lifecycle, settingsStorageManager);
lifecycle, userService, storageDefinitionId);
regattaRaceStatesComponent = new RegattaRaceStatesComponent(null, componentContext, sailingService, errorReporter,
stringMessages,
@@ -33,11 +33,10 @@ import com.sap.sse.gwt.client.player.Timer.PlayModes;
import com.sap.sse.gwt.client.player.Timer.PlayStates;
import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.OnSettingsLoadedCallback;
import com.sap.sse.gwt.settings.SettingsToUrlSerializer;
import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.settings.StorageDefinitionId;
import com.sap.sse.security.ui.settings.UserSettingsStorageManager;
public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
public static final long DEFAULT_REFRESH_INTERVAL_MILLIS = 3000l;
@@ -117,15 +116,13 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
// make a single live request as the default but don't continue to play by default
final StorageDefinitionId storageDefinitionId = StorageDefinitionIdFactory.createStorageDefinitionIdForLeaderboard(leaderboardContextDefinition);
final UserSettingsStorageManager<PerspectiveCompositeSettings<LeaderboardPerspectiveOwnSettings>> settingsManager = new UserSettingsStorageManager<>(
getUserService(), storageDefinitionId);
if (leaderboardType.isMetaLeaderboard()) {
// overall
MetaLeaderboardPerspectiveLifecycle rootComponentLifeCycle = new MetaLeaderboardPerspectiveLifecycle(
stringmessages);
ComponentContext<PerspectiveCompositeSettings<LeaderboardPerspectiveOwnSettings>> context = new ComponentContextWithSettingsStorage<>(
rootComponentLifeCycle, settingsManager);
rootComponentLifeCycle, getUserService(), storageDefinitionId);
context.getInitialSettings(
new OnSettingsLoadedCallback<PerspectiveCompositeSettings<LeaderboardPerspectiveOwnSettings>>() {
@Override
@@ -153,7 +150,7 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
LeaderboardPerspectiveLifecycle rootComponentLifeCycle = new LeaderboardPerspectiveLifecycle(
StringMessages.INSTANCE);
ComponentContext<PerspectiveCompositeSettings<LeaderboardPerspectiveOwnSettings>> context = new ComponentContextWithSettingsStorage<>(
rootComponentLifeCycle, settingsManager);
rootComponentLifeCycle, getUserService(), storageDefinitionId);
context.getInitialSettings(
new OnSettingsLoadedCallback<PerspectiveCompositeSettings<LeaderboardPerspectiveOwnSettings>>() {
@Override
@@ -8,12 +8,13 @@ import com.sap.sse.common.settings.util.SettingsMergeUtils;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.OnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorageAndPatching;
import com.sap.sse.security.ui.settings.SettingsPatch;
import com.sap.sse.security.ui.settings.UserSettingsStorageManagerWithPatching;
import com.sap.sse.security.ui.settings.StorageDefinitionId;
/**
* A specialization of {@link ComponentContextWithSettingsStorageAndPatching} which is specially designed for
@@ -32,13 +33,15 @@ public class RaceBoardComponentContext extends ComponentContextWithSettingsStora
/**
* @param rootLifecycle
* The {@link ComponentLifecycle} of the root component/perspective
* @param settingsStorageManager
* The {@link UserSettingsStorageManagerWithPatching} to be used to access stored settings and to store new settings
* @param userService
* The service which is used for server-side settings storage
* @param storageDefinitionId
* The definition for User Settings and Document Settings storage keys
*/
public RaceBoardComponentContext(
RaceBoardPerspectiveLifecycle rootLifecycle,
UserSettingsStorageManagerWithPatching<PerspectiveCompositeSettings<RaceBoardPerspectiveOwnSettings>> settingsStorageManager) {
super(rootLifecycle, settingsStorageManager);
UserService userService, StorageDefinitionId storageDefinitionId) {
super(rootLifecycle, userService, storageDefinitionId);
}
/**
@@ -30,11 +30,10 @@ import com.sap.sse.gwt.client.player.Timer;
import com.sap.sse.gwt.client.player.Timer.PlayModes;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.DefaultOnSettingsLoadedCallback;
import com.sap.sse.gwt.settings.SettingsToUrlSerializer;
import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.security.ui.settings.StorageDefinitionId;
import com.sap.sse.security.ui.settings.UserSettingsStorageManagerWithPatching;
public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
private RaceWithCompetitorsDTO selectedRace;
@@ -103,7 +102,7 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
final boolean showChartMarkEditMediaButtonsAndVideo = !DeviceDetector.isMobile();
final StorageDefinitionId storageDefinitionId = StorageDefinitionIdFactory.createStorageDefinitionIdForRaceBoard(raceboardContextDefinition);
final RaceBoardPerspectiveLifecycle lifeCycle = new RaceBoardPerspectiveLifecycle(raceboardData.getRace().getRaceIdentifier(), StringMessages.INSTANCE);
RaceBoardComponentContext componentContext = new RaceBoardComponentContext(lifeCycle, new UserSettingsStorageManagerWithPatching<>(getUserService(), storageDefinitionId));
RaceBoardComponentContext componentContext = new RaceBoardComponentContext(lifeCycle, getUserService(), storageDefinitionId);
componentContext.getInitialSettings(new DefaultOnSettingsLoadedCallback<PerspectiveCompositeSettings<RaceBoardPerspectiveOwnSettings>>() {
@Override
@@ -10,7 +10,6 @@ import javax.xml.bind.DatatypeConverter;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage;
@@ -74,127 +73,11 @@ public class SettingsTest extends AbstractSeleniumTest {
super.setUp();
}
//FIXME just for debugging the loading of raceboard in hudson
@Test
public void testRaceBoardPageWithoutMode() throws InterruptedException, UnsupportedEncodingException {
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
EventConfigurationPanelPO events = adminConsole.goToEvents();
events.createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(BMW_CUP_EVENT, BMW_CUP_EVENTS_DESC,
BMW_VENUE, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, true, BMW_CUP_REGATTA, BMW_CUP_BOAT_CLASS,
BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, false);
initTrackingForBmwCupRace(adminConsole);
RaceBoardPage raceboard = RaceBoardPage.goToRaceboardUrl(getWebDriver(), getContextRoot(), BMW_CUP_REGATTA,
BMW_CUP_REGATTA, String.format(BMW_RACE, 1));
MapSettingsPO mapSettings = raceboard.openMapSettings();
mapSettings.pressCancel();
LeaderboardSettingsDialogPO leaderboardSettingsDialog = raceboard.openLeaderboardSettingsDialog();
LeaderboardSettingsPanelPO leaderboardSettingsPanelPO = leaderboardSettingsDialog.getLeaderboardSettingsPanelPO();
leaderboardSettingsPanelPO.getSelectedDetails();
leaderboardSettingsDialog.pressCancel();
}
//FIXME just for debugging the loading of raceboard in hudson
@Test
public void testRaceBoardPageStartAnalysis() throws InterruptedException, UnsupportedEncodingException {
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
EventConfigurationPanelPO events = adminConsole.goToEvents();
events.createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(BMW_CUP_EVENT, BMW_CUP_EVENTS_DESC,
BMW_VENUE, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, true, BMW_CUP_REGATTA, BMW_CUP_BOAT_CLASS,
BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, false);
initTrackingForBmwCupRace(adminConsole);
RaceBoardPage raceboard = RaceBoardPage.goToRaceboardUrl(getWebDriver(), getContextRoot(), BMW_CUP_REGATTA,
BMW_CUP_REGATTA, String.format(BMW_RACE, 1), "START_ANALYSIS");
MapSettingsPO mapSettings = raceboard.openMapSettings();
mapSettings.pressCancel();
LeaderboardSettingsDialogPO leaderboardSettingsDialog = raceboard.openLeaderboardSettingsDialog();
LeaderboardSettingsPanelPO leaderboardSettingsPanelPO = leaderboardSettingsDialog.getLeaderboardSettingsPanelPO();
leaderboardSettingsPanelPO.getSelectedDetails();
leaderboardSettingsDialog.pressCancel();
}
//FIXME just for debugging the loading of raceboard in hudson
@Test
public void testRaceBoardPageFullAnalysisMode() throws InterruptedException, UnsupportedEncodingException {
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
EventConfigurationPanelPO events = adminConsole.goToEvents();
events.createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(BMW_CUP_EVENT, BMW_CUP_EVENTS_DESC,
BMW_VENUE, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, true, BMW_CUP_REGATTA, BMW_CUP_BOAT_CLASS,
BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, false);
initTrackingForBmwCupRace(adminConsole);
RaceBoardPage raceboard = RaceBoardPage.goToRaceboardUrl(getWebDriver(), getContextRoot(), BMW_CUP_REGATTA,
BMW_CUP_REGATTA, String.format(BMW_RACE, 1), "FULL_ANALYSIS");
MapSettingsPO mapSettings = raceboard.openMapSettings();
mapSettings.pressCancel();
LeaderboardSettingsDialogPO leaderboardSettingsDialog = raceboard.openLeaderboardSettingsDialog();
LeaderboardSettingsPanelPO leaderboardSettingsPanelPO = leaderboardSettingsDialog.getLeaderboardSettingsPanelPO();
leaderboardSettingsPanelPO.getSelectedDetails();
leaderboardSettingsDialog.pressCancel();
}
//FIXME just for debugging the loading of raceboard in hudson
@Test
public void testRaceBoardPageWinningLanesMode() throws InterruptedException, UnsupportedEncodingException {
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
EventConfigurationPanelPO events = adminConsole.goToEvents();
events.createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(BMW_CUP_EVENT, BMW_CUP_EVENTS_DESC,
BMW_VENUE, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, true, BMW_CUP_REGATTA, BMW_CUP_BOAT_CLASS,
BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, false);
initTrackingForBmwCupRace(adminConsole);
RaceBoardPage raceboard = RaceBoardPage.goToRaceboardUrl(getWebDriver(), getContextRoot(), BMW_CUP_REGATTA,
BMW_CUP_REGATTA, String.format(BMW_RACE, 1), "WINNING_LANES");
MapSettingsPO mapSettings = raceboard.openMapSettings();
mapSettings.pressCancel();
LeaderboardSettingsDialogPO leaderboardSettingsDialog = raceboard.openLeaderboardSettingsDialog();
LeaderboardSettingsPanelPO leaderboardSettingsPanelPO = leaderboardSettingsDialog.getLeaderboardSettingsPanelPO();
leaderboardSettingsPanelPO.getSelectedDetails();
leaderboardSettingsDialog.pressCancel();
}
//FIXME just for debugging the loading of raceboard in hudson
@Test
public void testRaceBoardPagePlayerMode() throws InterruptedException, UnsupportedEncodingException {
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
EventConfigurationPanelPO events = adminConsole.goToEvents();
events.createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(BMW_CUP_EVENT, BMW_CUP_EVENTS_DESC,
BMW_VENUE, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, true, BMW_CUP_REGATTA, BMW_CUP_BOAT_CLASS,
BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, false);
initTrackingForBmwCupRace(adminConsole);
RaceBoardPage raceboard = RaceBoardPage.goToRaceboardUrl(getWebDriver(), getContextRoot(), BMW_CUP_REGATTA,
BMW_CUP_REGATTA, String.format(BMW_RACE, 1), "PLAYER");
MapSettingsPO mapSettings = raceboard.openMapSettings();
mapSettings.pressCancel();
LeaderboardSettingsDialogPO leaderboardSettingsDialog = raceboard.openLeaderboardSettingsDialog();
LeaderboardSettingsPanelPO leaderboardSettingsPanelPO = leaderboardSettingsDialog.getLeaderboardSettingsPanelPO();
leaderboardSettingsPanelPO.getSelectedDetails();
leaderboardSettingsDialog.pressCancel();
}
/**
* Verifies the settings storage of the raceboard. Checks the precedences of url, context specific settings, mode settings and
* global settings.
*/
@Test
@Ignore
public void testRaceBoardPageSettingsStorage() throws InterruptedException, UnsupportedEncodingException {
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
EventConfigurationPanelPO events = adminConsole.goToEvents();
@@ -61,7 +61,7 @@ public class SettingsDialog<SettingsType extends Settings> extends AbstractSetti
new SettingsDialogCallback<>(component, callback));
if (component.getComponentContext() != null
&& component.getComponentContext().hasMakeCustomDefaultSettingsSupport(component)) {
&& component.getComponentContext().isStorageSupported(component)) {
initMakeDefaultButtons(component, stringMessages);
}
}
@@ -117,7 +117,7 @@ public class SettingsDialog<SettingsType extends Settings> extends AbstractSetti
@Override
public void ok(SettingsType editedSettings) {
if (component.getComponentContext() != null
&& component.getComponentContext().hasMakeCustomDefaultSettingsSupport(component)) {
&& component.getComponentContext().isStorageSupported(component)) {
component.getComponentContext().storeSettingsForContext(component, editedSettings, new DummyOnSettingsStoredCallback());
}
component.updateSettings(editedSettings);
@@ -27,15 +27,14 @@ public abstract class AbstractPerspectiveComposite<PL extends PerspectiveLifecyc
private final Map<String, Component<? extends Settings>> childComponents = new HashMap<>();
/**
* Adds the provided component as a child to this perspective and sets the {@link ComponentTreeNodeInfo}
* of the provided component accordingly.
* Adds the provided component as a child to the maintained component tree of this perspective.
*
* @param childComponent The component to be added as a child to this perspective
*/
protected void addChildComponent(Component<? extends Settings> childComponent) {
Component<? extends Settings> old = childComponents.put(childComponent.getId(), childComponent);
if (old != null) {
throw new IllegalStateException("Child with same id is allready added");
throw new IllegalStateException("Child with same id is already added");
}
}
@@ -1,27 +1,32 @@
package com.sap.sse.gwt.client.shared.perspective;
import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings;
import com.sap.sse.common.settings.generic.BooleanSetting;
public class IgnoreLocalSettings extends AbstractGenericSerializableSettings {
private static final long serialVersionUID = -282780795782313106L;
private transient BooleanSetting ignoreLocalSettings;
public IgnoreLocalSettings() {
}
public IgnoreLocalSettings(boolean ignoreLocalSettings) {
this.ignoreLocalSettings.setValue(ignoreLocalSettings);
}
@Override
protected void addChildSettings() {
ignoreLocalSettings = new BooleanSetting("ignoreLocalSettings", this, false);
}
public boolean isIgnoreLocalSettings() {
return Boolean.TRUE.equals(ignoreLocalSettings.getValue());
}
}
package com.sap.sse.gwt.client.shared.perspective;
import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings;
import com.sap.sse.common.settings.generic.BooleanSetting;
import com.sap.sse.gwt.settings.SettingsToUrlSerializer;
public class IgnoreLocalSettings extends AbstractGenericSerializableSettings {
private static final long serialVersionUID = -282780795782313106L;
private transient BooleanSetting ignoreLocalSettings;
public IgnoreLocalSettings() {
}
public IgnoreLocalSettings(boolean ignoreLocalSettings) {
this.ignoreLocalSettings.setValue(ignoreLocalSettings);
}
@Override
protected void addChildSettings() {
ignoreLocalSettings = new BooleanSetting("ignoreLocalSettings", this, false);
}
public boolean isIgnoreLocalSettings() {
return Boolean.TRUE.equals(ignoreLocalSettings.getValue());
}
public static IgnoreLocalSettings getIgnoreLocalSettingsFromCurrentUrl() {
return new SettingsToUrlSerializer().deserializeFromCurrentLocation(new IgnoreLocalSettings());
}
}
@@ -1,63 +0,0 @@
package com.sap.sse.gwt.client.shared.settings;
import com.sap.sse.common.settings.Settings;
/**
* Basic implementation for {@link SettingsBuildingPipeline} which provides a implementation of few methods.
* It is highly recommended to inherit all other implementations from this class.
*
* @author Vladislav Chumak
*
* @see SettingsBuildingPipeline
*
*/
public abstract class AbstractSettingsBuildingPipeline implements SettingsBuildingPipeline {
/**
* Conversion helper which is used by this instance for type conversion/serialization
* between settings objects and JSON Strings.
*/
protected final SettingsStringConverter settingsStringConverter;
/**
* Constructs an instance with {@link SettingsStringConverter} as conversion helper between
* settings objects and its JSON representation.
*/
public AbstractSettingsBuildingPipeline() {
this(new SettingsStringConverter());
}
/**
* Constructs an instance with a custom conversion helper between
* settings objects and its JSON representation.
*/
public AbstractSettingsBuildingPipeline(SettingsStringConverter settingsStringConverter) {
this.settingsStringConverter = settingsStringConverter;
}
/**
* {@inheritDoc}
*/
@Override
public<S extends Settings> S getSettingsObject(S defaultSettings, SettingsStrings settingsStrings) {
SettingsJsons settingsJsons = settingsStringConverter.convertToSettingsJson(settingsStrings);
return getSettingsObject(defaultSettings, settingsJsons);
}
/**
* {@inheritDoc}
*/
@Override
public <S extends Settings> S getSettingsObject(S defaultSettings) {
return getSettingsObject(defaultSettings, new SettingsJsons(null, null));
}
/**
* {@inheritDoc}
*/
@Override
public SettingsStringConverter getSettingsStringConverter() {
return settingsStringConverter;
}
}
@@ -37,7 +37,7 @@ public interface ComponentContext<S extends Settings> {
/**
* Stores the provided {@link Settings} of the provided {@link Component} as "User Settings" globally. Make sure to
* call this method only when {@link #hasMakeCustomDefaultSettingsSupport(Component)} method returns {@code true}
* call this method only when {@link #isStorageSupported(Component)} method returns {@code true}
* for the provided {@link Component}.
*
* @param component
@@ -50,7 +50,7 @@ public interface ComponentContext<S extends Settings> {
/**
* Stores the provided {@link Settings} of the provided {@link Component} as "Document Settings" (old term "Context specific Settings") for the current context. Make sure to
* call this method only when {@link #hasMakeCustomDefaultSettingsSupport(Component)} method returns {@code true}
* call this method only when {@link #isStorageSupported(Component)} method returns {@code true}
* for the provided {@link Component}.
*
* @param component
@@ -79,7 +79,7 @@ public interface ComponentContext<S extends Settings> {
* <b>AND</b> the {@link ComponentContext} implementation has settings
* storage support
*/
boolean hasMakeCustomDefaultSettingsSupport(Component<?> component);
boolean isStorageSupported(Component<?> component);
/**
* Gets initial settings and passes these settings to the provided callback.
@@ -1,335 +0,0 @@
package com.sap.sse.gwt.client.shared.settings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.common.settings.generic.GenericSerializableSettings;
import com.sap.sse.common.settings.generic.SettingsMap;
import com.sap.sse.common.settings.util.SettingsDefaultValuesUtils;
import com.sap.sse.common.settings.util.SettingsMergeUtils;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
/**
* Manages all the default/initial settings of a component/perspective and its subcomponents if there are any. It supplies
* components with initial settings and determines whether the settings of components are storable or not. This
* implementation provides settings storage support if the storable settings either implement {@link SettingsMap} or
* {@link GenericSerializableSettings}. There are two kinds of settings which are stored separately - <b>User
* Settings</b> (old term "Global Settings") and <b>Document Settings</b> (old term "Context specific Settings").
* <dl>
* <dt>Document Settings</dt>
* <dd>The settings are stored only for the current context (e.g. a race presented in a RaceBoard). This settings are not considered in any other context. See {@link #storeSettingsForContext(Component, Settings, OnSettingsStoredCallback)}</dd>
*
* <dt>User Settings</dt>
* <dd>The settings are stored for a component/perspective context-independently and may effect a different context presented by the same perspective/component, e.g. a different race in a RaceBoard. See {@link #makeSettingsDefault(Component, Settings, OnSettingsStoredCallback)}</dd>
* </dl>
* That means that Document Settings are stored per context (e.g. race in RaceBoard) whereas User Settings are
* stored globally for all possible contexts (e.g. for the whole RaceBoard independently from selected race).
*
*
* @author Vladislav Chumak
* @see ComponentContext
*
* @param <S>
* The {@link Settings} type of the settings of the root component/perspective containing all the settings
* for itself and its subcomponents
*
*/
public class ComponentContextWithSettingsStorage<S extends Settings> extends SimpleComponentContext<S> {
/**
* Settings which have been already constructed, or {@code null} if settings have not been initialized yet.
*/
protected S cachedRootComponentSettings = null;
/**
* Manages the persistence layer of settings.
*/
protected final SettingsStorageManager<S> settingsStorageManager;
/**
* @param rootLifecycle
* The {@link ComponentLifecycle} of the root component/perspective
* @param settingsStorageManager
* The {@link SettingsStorageManager} to be used to access stored settings and to store new settings
*/
public ComponentContextWithSettingsStorage(ComponentLifecycle<S> rootLifecycle,
SettingsStorageManager<S> settingsStorageManager) {
super(rootLifecycle);
this.settingsStorageManager = settingsStorageManager;
}
/**
* Retrieves the initial settings and caches them for further calls of
* {@link #getInitialSettings(OnSettingsLoadedCallback)} and {@link #getInitialSettingsForComponent(Component, OnSettingsLoadedCallback)}.
*/
public void initialize() {
getInitialSettings(new OnSettingsLoadedCallback<S>() {
@Override
public void onError(Throwable caught, S fallbackDefaultSettings) {}
@Override
public void onSuccess(S settings) {}
});
}
/**
* Gets initial settings and passes these settings to the provided callback.
* The provided callback gets called when initial/default settings are available. The callback may be called after a delay, e.g. after the settings have been retrieved from a server,
* or immediately, e.g. when the implementation does not query information from server.
* This method produces no side-effects if it gets called multiple times. After this method has been called for the first time, subsequent calls will not produce any server round-trips
* or LocalStorage accesses due to its internal caching.
*
* @param settingsReceiverCallback The callback which supplies the caller with initial settings
*/
@Override
public void getInitialSettings(final OnSettingsLoadedCallback<S> callback) {
if(cachedRootComponentSettings == null) {
S systemDefaultSettings = rootLifecycle.createDefaultSettings();
settingsStorageManager.retrieveDefaultSettings(systemDefaultSettings, new OnSettingsLoadedCallback<S>() {
@Override
public void onError(Throwable caught, S fallbackDefaultSettings) {
cachedRootComponentSettings = fallbackDefaultSettings;
callback.onError(caught, fallbackDefaultSettings);
}
@Override
public void onSuccess(S settings) {
cachedRootComponentSettings = settings;
callback.onSuccess(settings);
}
});
} else {
callback.onSuccess(cachedRootComponentSettings);
}
}
/**
* Gets initial settings of the provided component and passes these settings to the provided callback.
* The provided callback gets called when initial/default settings are available. The callback may be called after a delay, e.g. after the settings have been retrieved from a server,
* or immediately, e.g. when the implementation does not query information from server.
* This method produces no side-effects if it gets called multiple times.
* @param settingsReceiverCallback The callback which supplies the caller with initial settings
*/
@Override
public <CS extends Settings> void getInitialSettingsForComponent(final Component<CS> component, final OnSettingsLoadedCallback<CS> callback) {
OnSettingsLoadedCallback<S> internalCallback = new OnSettingsLoadedCallback<S>() {
@Override
public void onError(Throwable caught, S fallbackDefaultSettings) {
CS componentFallbackSettings = ComponentUtils.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(component.getPath()), fallbackDefaultSettings);
callback.onError(caught, componentFallbackSettings);
}
@Override
public void onSuccess(S settings) {
CS componentSettings = ComponentUtils.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(component.getPath()), settings);
callback.onSuccess(componentSettings);
}
};
getInitialSettings(internalCallback);
}
protected void invalidateCachedSettings() {
cachedRootComponentSettings = null;
}
/**
* Checks whether the {@link Settings} of the provided {@link Component} are
* storable and whether the underlying implementation supports
* {@link #makeSettingsDefault(Component, Settings)} calls for it.
*
* @param component The component with potentially storable settings
* @return {@code true} if the settings of the component are storable
* <b>AND</b> the {@link ComponentContext} implementation has settings
* storage support
*/
@Override
public boolean hasMakeCustomDefaultSettingsSupport(Component<?> component) {
if (!component.hasSettings()) {
return false;
}
if(!settingsStorageManager.supportsStore()) {
return false;
}
Settings settings = component.getSettings();
if (settings instanceof SettingsMap || settings instanceof GenericSerializableSettings) {
return true;
}
return false;
}
/**
* Stores the provided {@link Settings} of the provided {@link Component} as "User Settings" globally. Make sure to
* call this method only when {@link #hasMakeCustomDefaultSettingsSupport(Component)} method returns {@code true}
* for the provided {@link Component}.
*
* @param component
* The component which corresponds to the provided {@link Settings}
* @param newDefaultSettings
* The {@link Settings} to be stored
* @param onSettingsStoredCallback The callback which is called when the settings storage process finishes
*/
@Override
public<CS extends Settings> void makeSettingsDefault(Component<CS> component, CS newDefaultSettings, final OnSettingsStoredCallback onSettingsStoredCallback) {
invalidateCachedSettings();
ComponentLifecycle<CS> targetLifecycle = ComponentUtils.determineLifecycle(new ArrayList<>(component.getPath()), rootLifecycle);
CS globalSettings = targetLifecycle.extractGlobalSettings(newDefaultSettings);
updateGlobalSettings(component.getPath(), globalSettings, onSettingsStoredCallback);
}
/**
* Stores the provided {@link Settings} of the provided {@link Component} as "Document Settings" (old term "Context specific Settings") for the current context. Make sure to
* call this method only when {@link #hasMakeCustomDefaultSettingsSupport(Component)} method returns {@code true}
* for the provided {@link Component}.
*
* @param component
* The component which corresponds to the provided {@link Settings}
* @param newSettings
* The {@link Settings} to be stored
* @param onSettingsStoredCallback The callback which is called when the settings storage process finishes
*/
@Override
public<CS extends Settings> void storeSettingsForContext(Component<CS> component, CS newSettings, OnSettingsStoredCallback onSettingsStoredCallback) {
invalidateCachedSettings();
ComponentLifecycle<CS> targetLifecycle = ComponentUtils.determineLifecycle(new ArrayList<>(component.getPath()), rootLifecycle);
CS contextSpecificSettings = targetLifecycle.extractContextSpecificSettings(newSettings);
updateContextSpecificSettings(component.getPath(), contextSpecificSettings, onSettingsStoredCallback);
}
/**
* Patches the settings tree with the new settings provided. The settings node with the provided path
* is going to be created/replaced with the new settings.
*
* @param root The root node of the settings tree
* @param path The path of the node to create/update
* @param newSettings The new settings with that the target node in the settings tree is going to be updated
* @param pipelineLevel The scope of settings used for settings storage (e.g. User Settings, or Document Settings)
* @return The patched settings located by provided path as JSON
*/
private JSONObject patchJsonObject(JSONObject root, List<String> path, Settings newSettings, PipelineLevel pipelineLevel) {
newSettings = cloneStorableSettings(newSettings, path);
return patchJsonObject(root, path, Collections.unmodifiableList(new ArrayList<>(path)), newSettings, pipelineLevel);
}
@SuppressWarnings("unchecked")
private<CS extends Settings> CS cloneStorableSettings(CS settingsToClone, List<String> path) {
CS clonedSettings = ComponentUtils.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(path), rootLifecycle.createDefaultSettings());
if(clonedSettings instanceof GenericSerializableSettings) {
GenericSerializableSettings newSettings = (GenericSerializableSettings) clonedSettings;
GenericSerializableSettings originalSettings = (GenericSerializableSettings) settingsToClone;
SettingsDefaultValuesUtils.keepDefaults(originalSettings, newSettings);
SettingsMergeUtils.setValues(originalSettings, newSettings);
clonedSettings = (CS) newSettings;
}
//TODO maybe add support for SettingsMap? Currently, SettingsMap is never provided as parameter to this method
return clonedSettings;
}
/**
* Internal helper method for {@link #patchJsonObject(JSONObject, List, Settings, PipelineLevel)}
*
* @param root
* @param path
* @param originalPath
* @param newSettings
* @param pipelineLevel
* @return
*/
private JSONObject patchJsonObject(JSONObject root, List<String> path, List<String> originalPath, Settings newSettings, PipelineLevel pipelineLevel) {
if(path.isEmpty()) {
return (JSONObject) settingsStorageManager.settingsToJSON(newSettings, pipelineLevel, originalPath);
}
if (root == null) {
root = new JSONObject();
}
String current = path.remove(path.size() - 1);
// we need to go further
if (!path.isEmpty()) {
JSONValue child = root.get(current);
boolean haskey = root.containsKey(current);
if (child == null || child.isObject() == null) {
if (haskey) {
GWT.log("Warning: replacing some subtree element that is wrong type!");
}
child = new JSONObject();
root.put(current, child);
}
return patchJsonObject(child.isObject(), path, originalPath, newSettings, pipelineLevel);
} else {
JSONValue json = settingsStorageManager.settingsToJSON(newSettings, pipelineLevel, originalPath);
root.put(current, json);
}
return root;
}
/**
* Updates the User Settings' tree with the new settings provided. The settings node with the provided path
* is going to be created/replaced with the new settings. This method may produce a server round-trip
* if the user is logged in.
*
* @param path The path of the settings' node to create/update
* @param globalSettings User Settings which corresponds to the provided path
* @param onSettingsStoredCallback The callback which is called when the settings storage process finishes
*/
private void updateGlobalSettings(final ArrayList<String> path, final Settings globalSettings,
final OnSettingsStoredCallback onSettingsStoredCallback) {
settingsStorageManager.retrieveSettingsJsons(new AsyncCallback<SettingsJsons>() {
@Override
public void onSuccess(SettingsJsons result) {
final JSONObject patchedGlobalSettings = patchJsonObject(result.getGlobalSettingsJson(), new ArrayList<>(path),
globalSettings, PipelineLevel.GLOBAL_DEFAULTS);
settingsStorageManager.storeGlobalSettingsJson(patchedGlobalSettings, onSettingsStoredCallback);
}
@Override
public void onFailure(Throwable caught) {
onSettingsStoredCallback.onError(caught);
}
});
}
/**
* Updates the Document Settings' tree with the new settings provided. The settings node with the provided path
* is going to be created/replaced with the new settings. This method may produce a server round-trip
* if the user is logged in.
*
* @param path The path of the settings' node to create/update
* @param contextSpecificSettings Document Settings which corresponds to the provided path
* @param onSettingsStoredCallback The callback which is called when the settings storage process finishes
*/
private void updateContextSpecificSettings(final ArrayList<String> path, final Settings contextSpecificSettings,
final OnSettingsStoredCallback onSettingsStoredCallback) {
settingsStorageManager.retrieveSettingsJsons(new AsyncCallback<SettingsJsons>() {
@Override
public void onSuccess(SettingsJsons result) {
final JSONObject patchedContextSpecificSettings = patchJsonObject(result.getContextSpecificSettingsJson(), new ArrayList<>(path),
contextSpecificSettings, PipelineLevel.CONTEXT_SPECIFIC_DEFAULTS);
settingsStorageManager.storeContextSpecificSettingsJson(patchedContextSpecificSettings, onSettingsStoredCallback);
}
@Override
public void onFailure(Throwable caught) {
onSettingsStoredCallback.onError(caught);
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
super.dispose();
settingsStorageManager.dispose();
}
}
@@ -1,8 +1,6 @@
package com.sap.sse.gwt.client.shared.settings;
import com.sap.sse.common.settings.Settings;
public interface OnSettingsLoadedCallback<S extends Settings> {
public interface OnSettingsLoadedCallback<S> {
void onError(Throwable caught, S fallbackDefaultSettings);
void onSuccess(S settings);
}
@@ -11,48 +11,25 @@ import com.sap.sse.common.settings.Settings;
* implementation of this interface to define the desired way of how the settings are constructed from its persisted
* JSON representation, and how the settings are transformed back into its persisted JSON representation.
* The pipeline is being used in {@link SettingsStorageManager} when it loads and stores settings.
* It is highly recommended to use at least {@link AbstractSettingsBuildingPipeline} as basic class for
* implementation of this interface.
*
* @author Vladislav Chumak
*
* @param <S> The type of settings representation
* @see SettingsStorageManager
* @see AbstractSettingsBuildingPipeline
*
*/
public interface SettingsBuildingPipeline {
public interface SettingsBuildingPipeline<S> {
/**
* Constructs a settings object by means of provided defaultSettings and persisted representations of
* User Settings and Document Settings.
* Constructs a settings object by means of provided defaultSettings and persisted representations of Settings.
* This method implements the settings construction pipeline for a settings object which is used for settings loading operations.
*
* @param defaultSettings The basic settings to be used
* @param settingsJsons The persisted representation of User Settings and Document Settings
* @param settingsRepresentation The persisted representation of Settings
* @return The constructed settings object
*/
<S extends Settings> S getSettingsObject(S defaultSettings, SettingsJsons settingsJsons);
/**
* Constructs a settings object by means of provided defaultSettings and persisted representations of
* User Settings and Document Settings.
* This method implements the settings construction pipeline for a settings object which is used for settings loading operations.
*
* @param defaultSettings The basic settings to be used
* @param settingsJsons The persisted representation of User Settings and Document Settings
* @return The constructed settings object
*/
<S extends Settings> S getSettingsObject(S defaultSettings, SettingsStrings settingsStrings);
/**
* Constructs a settings object by means of provided defaultSettings without considering
* User Settings and Document Settings.
* This method implements the settings construction pipeline for a settings object which is used for settings loading operations.
*
* @param defaultSettings The basic settings to be used
* @return The constructed settings object
*/
<S extends Settings> S getSettingsObject(S defaultSettings);
<CS extends Settings> CS getSettingsObject(CS defaultSettings, S settingsRepresentation);
/**
* Converts the provided settings according the storage scope and path of the provided settings in the settings tree.
@@ -65,12 +42,4 @@ public interface SettingsBuildingPipeline {
*/
JSONValue getJsonObject(Settings settings, PipelineLevel pipelineLevel, List<String> path);
/**
* Gets the {@link SettingsStringConverter} which is used by this instance for type conversion/serialization
* between settings objects and JSON Strings.
*
* @return The conversion helper instance used by this pipeline
*/
SettingsStringConverter getSettingsStringConverter();
}
@@ -9,22 +9,21 @@ import com.sap.sse.gwt.settings.SettingsToJsonSerializerGWT;
import com.sap.sse.gwt.settings.SettingsToUrlSerializer;
/**
* Conversion helper which is used by this instance for type conversion/serialization
* between settings objects and JSON Strings. The class encapsulates all serializers
* which are required for serialization and deserialization and provides methods
* for an indirect serialization/deserialization functionality required by
* {@link SettingsBuildingPipeline} and {@link SettingsStorageManager} implementations.
* Conversion helper which is used by this instance for type conversion/serialization between settings objects and JSON
* Strings. The class encapsulates all serializers which are required for serialization and deserialization and provides
* methods for an indirect serialization/deserialization functionality required by {@link SettingsBuildingPipeline} and
* {@link ComponentContext} implementations.
*
* @author Vladislav Chumak
*
*/
public class SettingsStringConverter {
public class SettingsSerializationHelper {
private final SettingsToUrlSerializer urlSerializer = new SettingsToUrlSerializer();
private final SettingsToJsonSerializerGWT jsonSerializer = new SettingsToJsonSerializerGWT();
@SuppressWarnings("unchecked")
public<S extends Settings> S deserializeFromCurrentUrl(S defaultSettings) {
public <S extends Settings> S deserializeFromCurrentUrl(S defaultSettings) {
if (defaultSettings instanceof GenericSerializableSettings) {
defaultSettings = (S) urlSerializer
.deserializeFromCurrentLocation((GenericSerializableSettings) defaultSettings);
@@ -34,9 +33,9 @@ public class SettingsStringConverter {
}
return defaultSettings;
}
@SuppressWarnings("unchecked")
public<S extends Settings> S deserializeFromJson(S defaultSettings, JSONObject jsonToDeserialize) {
public <S extends Settings> S deserializeFromJson(S defaultSettings, JSONObject jsonToDeserialize) {
if (defaultSettings instanceof GenericSerializableSettings) {
defaultSettings = (S) jsonSerializer.deserialize((GenericSerializableSettings) defaultSettings,
jsonToDeserialize);
@@ -46,31 +45,19 @@ public class SettingsStringConverter {
return defaultSettings;
}
public SettingsJsons convertToSettingsJson(SettingsStrings settingsStrings) {
JSONObject globalSettingsJson = convertStringToJson(settingsStrings.getGlobalSettingsString());
JSONObject contextSpecificSettingsJson = convertStringToJson(settingsStrings.getContextSpecificSettingsString());
return new SettingsJsons(globalSettingsJson, contextSpecificSettingsJson);
}
public JSONObject convertStringToJson(String str) {
return str == null ? null : jsonSerializer.parseStringToJsonObject(str);
}
public String convertJsonToString(JSONObject json) {
return json == null ? null : jsonSerializer.jsonObjectToString(json);
}
public SettingsStrings convertToSettingsStrings(SettingsJsons settingsJsons) {
String globalSettingsString = convertJsonToString(settingsJsons.getGlobalSettingsJson());
String contextSpecificSettingsString = convertJsonToString(settingsJsons.getContextSpecificSettingsJson());
return new SettingsStrings(globalSettingsString, contextSpecificSettingsString);
}
public JSONValue serializeFromSettingsObject(Settings newSettings) {
if (newSettings instanceof GenericSerializableSettings) {
return jsonSerializer.serialize((GenericSerializableSettings) newSettings);
}
throw new IllegalStateException("Requested save of settings that is not Serializable!");
}
}
@@ -1,11 +1,5 @@
package com.sap.sse.gwt.client.shared.settings;
import java.util.List;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.common.settings.generic.GenericSerializableSettings;
/**
@@ -16,66 +10,43 @@ import com.sap.sse.common.settings.generic.GenericSerializableSettings;
*
* @author Vladislav Chumak
*
* @param <S> The {@link Settings} type of the settings of the root component/perspective containing all the settings for itself and its subcomponents
* @param <S> The type of settings representation
* @see ComponentContextWithSettingsStorage
* @see GenericSerializableSettings
*/
public interface SettingsStorageManager<S extends Settings> {
public interface SettingsStorageManager<S> {
/**
* Tells whether the underlying implementation offers persistence for settings, or not.
*
* @return {@code True} if settings storage is supported, otherwise {@code false}
*/
boolean supportsStore();
// /**
// * Tells whether storage of settings is supported, or not.
// *
// * @return {@code True} if settings storage is supported, otherwise {@code false}
// */
// boolean supportsStore();
/**
* Retrieves the {@link Settings} of the root component using the provided callback.
* Depending on the storage media, the callback may be called whether synchronously
* or asynchronously.
*
* @param defaultSettings The system default settings for the root component
* @param asyncCallback The callback to be called when the settings have been retrieved
* from the settings storage
*/
void retrieveDefaultSettings(S defaultSettings,
OnSettingsLoadedCallback<S> asyncCallback);
/**
* Stores provided settings as User Settings (old term "Global Settings"). Only the delta between default settings and provided settings
* will be stored.
*
* @param globalSettingsJson The settings to store
* @param onSettingsStoredCallback The callback which is called when the settings storage process gets finished
*/
void storeGlobalSettingsJson(JSONObject globalSettingsJson, OnSettingsStoredCallback onSettingsStoredCallback);
/**
* Stores provided settings as Document Settings (old term "Context specific Settings"). Only the delta between default settings and provided settings
* will be stored.
*
* @param contextSpecificSettingsJson The settings to store
* @param onSettingsStoredCallback The callback which is called when the settings storage process gets finished
*/
void storeContextSpecificSettingsJson(JSONObject contextSpecificSettingsJson,
OnSettingsStoredCallback onSettingsStoredCallback);
/**
* Converts the provided settings object to JSON.
*
* @param newSettings The settings to convert
* @param pipelineLevel The pipeline level indicating whether the provided settings should be converted for Document Settings storage, or User Settings storage
* @param path The path of the corresponding settings in the settings tree according to the component which the provided settings belong to
* @return The converted settings as JSON
*/
JSONValue settingsToJSON(Settings newSettings, PipelineLevel pipelineLevel, List<String> path);
// /**
// * Converts the provided settings object to JSON.
// *
// * @param newSettings The settings to convert
// * @param pipelineLevel The pipeline level indicating whether the provided settings should be converted for Document Settings storage, or User Settings storage
// * @param path The path of the corresponding settings in the settings tree according to the component which the provided settings belong to
// * @return The converted settings as JSON
// */
// JSONValue settingsToJSON(Settings newSettings, PipelineLevel pipelineLevel, List<String> path);
/**
* Retrieves the stored JSON representations of Document Settings and User Settings which get packed together in {@link SettingsJsons}.
*
* @param asyncCallback The callback which gets called when the persisted settings representation has been retrieved
* @param callback The callback which gets called when the persisted settings representation has been retrieved
*/
void retrieveSettingsJsons(AsyncCallback<SettingsJsons> asyncCallback);
void retrieveSettingsRepresentation(OnSettingsLoadedCallback<S> callback);
/**
* Stores provided settings representation.
*
* @param settingsRepresentation The settings to store
* @param onSettingsStoredCallback The callback which is called when the settings storage process gets finished
*/
void storeSettingsRepresentation(S settingsRepresentation, OnSettingsStoredCallback onSettingsStoredCallback);
/**
* Releases all resources and listener registrations acquired by this instance.
@@ -0,0 +1,404 @@
package com.sap.sse.security.ui.settings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.common.settings.generic.GenericSerializableSettings;
import com.sap.sse.common.settings.generic.SettingsMap;
import com.sap.sse.common.settings.util.SettingsDefaultValuesUtils;
import com.sap.sse.common.settings.util.SettingsMergeUtils;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
import com.sap.sse.gwt.client.shared.perspective.IgnoreLocalSettings;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.client.shared.settings.ComponentUtils;
import com.sap.sse.gwt.client.shared.settings.OnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.OnSettingsStoredCallback;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.gwt.client.shared.settings.SettingsBuildingPipeline;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsSerializationHelper;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
import com.sap.sse.gwt.client.shared.settings.SettingsStrings;
import com.sap.sse.security.ui.client.UserService;
/**
* Manages all the default/initial settings of a component/perspective and its subcomponents if there are any. It
* supplies components with initial settings and determines whether the settings of components are storable or not. This
* implementation provides settings storage support if the storable settings either implement {@link SettingsMap} or
* {@link GenericSerializableSettings}. There are two kinds of settings which are stored separately - <b>User
* Settings</b> (old term "Global Settings") and <b>Document Settings</b> (old term "Context specific Settings").
* <dl>
* <dt>Document Settings</dt>
* <dd>The settings are stored only for the current context (e.g. a race presented in a RaceBoard). This settings are
* not considered in any other context. See
* {@link #storeSettingsForContext(Component, Settings, OnSettingsStoredCallback)}</dd>
*
* <dt>User Settings</dt>
* <dd>The settings are stored for a component/perspective context-independently and may effect a different context
* presented by the same perspective/component, e.g. a different race in a RaceBoard. See
* {@link #makeSettingsDefault(Component, Settings, OnSettingsStoredCallback)}</dd>
* </dl>
* That means that Document Settings are stored per context (e.g. race in RaceBoard) whereas User Settings are stored
* globally for all possible contexts (e.g. for the whole RaceBoard independently from selected race).
* <p>The underlying access to settings storage gets disabled if the URL contains {@code ignoreLocalSettings=true} flag.</p>
*
*
* @author Vladislav Chumak
* @see ComponentContext
*
* @param <S>
* The {@link Settings} type of the settings of the root component/perspective containing all the settings
* for itself and its subcomponents
*
*/
public class ComponentContextWithSettingsStorage<S extends Settings> extends SimpleComponentContext<S> {
/**
* {@code SettingsJsons} which have which have been already queried from persistence layer, or {@code null} if
* previous settings retrievement has not been initiated.
*/
protected SettingsJsons cachedSettingsJsons = null;
/**
* Manages the persistence layer of settings.
*/
protected final SettingsStorageManager<SettingsStrings> settingsStorageManager;
/**
* @param rootLifecycle
* The {@link ComponentLifecycle} of the root component/perspective
* @param userService
* The service which is used for server-side settings storage
* @param storageDefinitionId
* The definition for User Settings and Document Settings storage keys
*/
public ComponentContextWithSettingsStorage(ComponentLifecycle<S> rootLifecycle, UserService userService,
StorageDefinitionId storageDefinitionId) {
this(rootLifecycle, userService, storageDefinitionId, new SettingsSerializationHelper());
}
protected ComponentContextWithSettingsStorage(ComponentLifecycle<S> rootLifecycle, UserService userService,
StorageDefinitionId storageDefinitionId, SettingsSerializationHelper settingsSerializationHelper) {
this(rootLifecycle, userService, storageDefinitionId, settingsSerializationHelper,
new UserSettingsBuildingPipeline(settingsSerializationHelper));
}
protected ComponentContextWithSettingsStorage(ComponentLifecycle<S> rootLifecycle, UserService userService,
StorageDefinitionId storageDefinitionId, SettingsSerializationHelper settingsSerializationHelper,
SettingsBuildingPipeline<SettingsJsons> settingsBuildingPipeline) {
super(rootLifecycle, settingsSerializationHelper, settingsBuildingPipeline);
if(IgnoreLocalSettings.getIgnoreLocalSettingsFromCurrentUrl().isIgnoreLocalSettings()) {
this.settingsStorageManager = null;
} else {
this.settingsStorageManager = new UserSettingsStorageManager(userService, storageDefinitionId);
}
}
/**
* Retrieves the initial settings and caches them for further calls of
* {@link #getInitialSettings(OnSettingsLoadedCallback)} and
* {@link #getInitialSettingsForComponent(Component, OnSettingsLoadedCallback)}.
*/
public void initialize() {
getInitialSettings(new OnSettingsLoadedCallback<S>() {
@Override
public void onError(Throwable caught, S fallbackDefaultSettings) {
}
@Override
public void onSuccess(S settings) {
}
});
}
/**
* Gets initial settings and passes these settings to the provided callback. The provided callback gets called when
* initial/default settings are available. The callback may be called after a delay, e.g. after the settings have
* been retrieved from a server, or immediately, e.g. when the implementation does not query information from
* server. This method produces no side-effects if it gets called multiple times. After this method has been called
* for the first time, subsequent calls will not produce any server round-trips or LocalStorage accesses due to its
* internal caching.
*
* @param settingsReceiverCallback
* The callback which supplies the caller with initial settings
*/
@Override
public void getInitialSettings(final OnSettingsLoadedCallback<S> callback) {
final S systemDefaultSettings = rootLifecycle.createDefaultSettings();
if (cachedSettingsJsons == null) {
if(settingsStorageManager != null) {
settingsStorageManager.retrieveSettingsRepresentation(new OnSettingsLoadedCallback<SettingsStrings>() {
@Override
public void onError(Throwable caught, SettingsStrings fallbackSettingsRepresentation) {
callback.onError(caught, getSettingsObject(fallbackSettingsRepresentation, systemDefaultSettings));
}
@Override
public void onSuccess(SettingsStrings settingsRepresentation) {
callback.onSuccess(getSettingsObject(settingsRepresentation, systemDefaultSettings));
}
});
} else {
callback.onSuccess(getSettingsObject(new SettingsStrings(null, null), systemDefaultSettings));
}
} else {
S newDefaultSettings = settingsBuildingPipeline.getSettingsObject(systemDefaultSettings,
cachedSettingsJsons);
callback.onSuccess(newDefaultSettings);
}
}
private S getSettingsObject(SettingsStrings settingsRepresentation, S systemDefaultSettings) {
cachedSettingsJsons = convertToSettingsJson(settingsRepresentation);
S settingsObject = settingsBuildingPipeline.getSettingsObject(systemDefaultSettings,
cachedSettingsJsons);
return settingsObject;
}
/**
* Checks whether the {@link Settings} of the provided {@link Component} are storable and whether the underlying
* implementation enables {@link #makeSettingsDefault(Component, Settings)} calls for it.
*
* @param component
* The component with potentially storable settings
* @return {@code true} if the settings of the component are storable <b>AND</b> the {@link ComponentContext}
* implementation has settings storage support
*/
@Override
public boolean isStorageSupported(Component<?> component) {
if (!component.hasSettings()) {
return false;
}
if(settingsStorageManager == null) {
return false;
}
Settings settings = component.getSettings();
if (settings instanceof SettingsMap || settings instanceof GenericSerializableSettings) {
return true;
}
return false;
}
/**
* Stores the provided {@link Settings} of the provided {@link Component} as "User Settings" globally. Make sure to
* call this method only when {@link #isStorageSupported(Component)} method returns {@code true}
* for the provided {@link Component}.
*
* @param component
* The component which corresponds to the provided {@link Settings}
* @param newDefaultSettings
* The {@link Settings} to be stored
* @param onSettingsStoredCallback
* The callback which is called when the settings storage process finishes
*/
@Override
public <CS extends Settings> void makeSettingsDefault(Component<CS> component, CS newDefaultSettings,
final OnSettingsStoredCallback onSettingsStoredCallback) {
ComponentLifecycle<CS> targetLifecycle = ComponentUtils.determineLifecycle(new ArrayList<>(component.getPath()),
rootLifecycle);
CS globalSettings = targetLifecycle.extractGlobalSettings(newDefaultSettings);
updateGlobalSettings(component.getPath(), globalSettings, onSettingsStoredCallback);
}
/**
* Stores the provided {@link Settings} of the provided {@link Component} as "Document Settings" (old term "Context
* specific Settings") for the current context. Make sure to call this method only when
* {@link #isStorageSupported(Component)} method returns {@code true} for the provided
* {@link Component}.
*
* @param component
* The component which corresponds to the provided {@link Settings}
* @param newSettings
* The {@link Settings} to be stored
* @param onSettingsStoredCallback
* The callback which is called when the settings storage process finishes
*/
@Override
public <CS extends Settings> void storeSettingsForContext(Component<CS> component, CS newSettings,
OnSettingsStoredCallback onSettingsStoredCallback) {
ComponentLifecycle<CS> targetLifecycle = ComponentUtils.determineLifecycle(new ArrayList<>(component.getPath()),
rootLifecycle);
CS contextSpecificSettings = targetLifecycle.extractContextSpecificSettings(newSettings);
updateContextSpecificSettings(component.getPath(), contextSpecificSettings, onSettingsStoredCallback);
}
/**
* Patches the settings tree with the new settings provided. The settings node with the provided path is going to be
* created/replaced with the new settings.
*
* @param root
* The root node of the settings tree
* @param path
* The path of the node to create/update
* @param newSettings
* The new settings with that the target node in the settings tree is going to be updated
* @param pipelineLevel
* The scope of settings used for settings storage (e.g. User Settings, or Document Settings)
* @return The patched settings located by provided path as JSON
*/
private JSONObject patchJsonObject(JSONObject root, List<String> path, Settings newSettings,
PipelineLevel pipelineLevel) {
newSettings = cloneStorableSettings(newSettings, path);
return patchJsonObject(root, path, Collections.unmodifiableList(new ArrayList<>(path)), newSettings,
pipelineLevel);
}
@SuppressWarnings("unchecked")
private <CS extends Settings> CS cloneStorableSettings(CS settingsToClone, List<String> path) {
CS clonedSettings = ComponentUtils.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(path),
rootLifecycle.createDefaultSettings());
if (clonedSettings instanceof GenericSerializableSettings) {
GenericSerializableSettings newSettings = (GenericSerializableSettings) clonedSettings;
GenericSerializableSettings originalSettings = (GenericSerializableSettings) settingsToClone;
SettingsDefaultValuesUtils.keepDefaults(originalSettings, newSettings);
SettingsMergeUtils.setValues(originalSettings, newSettings);
clonedSettings = (CS) newSettings;
}
// TODO maybe add support for SettingsMap? Currently, SettingsMap is never provided as parameter to this method
return clonedSettings;
}
/**
* Internal helper method for {@link #patchJsonObject(JSONObject, List, Settings, PipelineLevel)}
*
* @param root
* @param path
* @param originalPath
* @param newSettings
* @param pipelineLevel
* @return
*/
private JSONObject patchJsonObject(JSONObject root, List<String> path, List<String> originalPath,
Settings newSettings, PipelineLevel pipelineLevel) {
if (path.isEmpty()) {
return (JSONObject) settingsBuildingPipeline.getJsonObject(newSettings, pipelineLevel, originalPath);
}
if (root == null) {
root = new JSONObject();
}
String current = path.remove(path.size() - 1);
// we need to go further
if (!path.isEmpty()) {
JSONValue child = root.get(current);
boolean haskey = root.containsKey(current);
if (child == null || child.isObject() == null) {
if (haskey) {
GWT.log("Warning: replacing some subtree element that is wrong type!");
}
child = new JSONObject();
root.put(current, child);
}
return patchJsonObject(child.isObject(), path, originalPath, newSettings, pipelineLevel);
} else {
JSONValue json = settingsBuildingPipeline.getJsonObject(newSettings, pipelineLevel, originalPath);
root.put(current, json);
}
return root;
}
/**
* Updates the User Settings' tree with the new settings provided. The settings node with the provided path is going
* to be created/replaced with the new settings. This method may produce a server round-trip if the user is logged
* in.
*
* @param path
* The path of the settings' node to create/update
* @param globalSettings
* User Settings which corresponds to the provided path
* @param onSettingsStoredCallback
* The callback which is called when the settings storage process finishes
*/
private void updateGlobalSettings(final ArrayList<String> path, final Settings globalSettings,
final OnSettingsStoredCallback onSettingsStoredCallback) {
settingsStorageManager.retrieveSettingsRepresentation(new OnSettingsLoadedCallback<SettingsStrings>() {
@Override
public void onError(Throwable caught, SettingsStrings fallbackDefaultSettings) {
onSettingsStoredCallback.onError(caught);
}
@Override
public void onSuccess(SettingsStrings settingsRepresentation) {
SettingsJsons result = convertToSettingsJson(settingsRepresentation);
final JSONObject patchedGlobalSettings = patchJsonObject(result.getGlobalSettingsJson(),
new ArrayList<>(path), globalSettings, PipelineLevel.GLOBAL_DEFAULTS);
settingsRepresentation = convertToSettingsStrings(new SettingsJsons(patchedGlobalSettings, null));
cachedSettingsJsons = new SettingsJsons(patchedGlobalSettings, cachedSettingsJsons.getContextSpecificSettingsJson());
settingsStorageManager.storeSettingsRepresentation(settingsRepresentation, onSettingsStoredCallback);
}
});
}
/**
* Updates the Document Settings' tree with the new settings provided. The settings node with the provided path is
* going to be created/replaced with the new settings. This method may produce a server round-trip if the user is
* logged in.
*
* @param path
* The path of the settings' node to create/update
* @param contextSpecificSettings
* Document Settings which corresponds to the provided path
* @param onSettingsStoredCallback
* The callback which is called when the settings storage process finishes
*/
private void updateContextSpecificSettings(final ArrayList<String> path, final Settings contextSpecificSettings,
final OnSettingsStoredCallback onSettingsStoredCallback) {
settingsStorageManager.retrieveSettingsRepresentation(new OnSettingsLoadedCallback<SettingsStrings>() {
@Override
public void onError(Throwable caught, SettingsStrings fallbackDefaultSettings) {
onSettingsStoredCallback.onError(caught);
}
@Override
public void onSuccess(SettingsStrings settingsRepresentation) {
SettingsJsons result = convertToSettingsJson(settingsRepresentation);
final JSONObject patchedContextSpecificSettings = patchJsonObject(
result.getContextSpecificSettingsJson(), new ArrayList<>(path), contextSpecificSettings,
PipelineLevel.CONTEXT_SPECIFIC_DEFAULTS);
settingsRepresentation = convertToSettingsStrings(
new SettingsJsons(null, patchedContextSpecificSettings));
cachedSettingsJsons = new SettingsJsons(cachedSettingsJsons.getGlobalSettingsJson(), patchedContextSpecificSettings);
settingsStorageManager.storeSettingsRepresentation(settingsRepresentation, onSettingsStoredCallback);
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
super.dispose();
if(settingsStorageManager != null) {
settingsStorageManager.dispose();
}
}
public SettingsJsons convertToSettingsJson(SettingsStrings settingsStrings) {
JSONObject globalSettingsJson = settingsSerializationHelper
.convertStringToJson(settingsStrings.getGlobalSettingsString());
JSONObject contextSpecificSettingsJson = settingsSerializationHelper
.convertStringToJson(settingsStrings.getContextSpecificSettingsString());
return new SettingsJsons(globalSettingsJson, contextSpecificSettingsJson);
}
public SettingsStrings convertToSettingsStrings(SettingsJsons settingsJsons) {
String globalSettingsString = settingsSerializationHelper
.convertJsonToString(settingsJsons.getGlobalSettingsJson());
String contextSpecificSettingsString = settingsSerializationHelper
.convertJsonToString(settingsJsons.getContextSpecificSettingsJson());
return new SettingsStrings(globalSettingsString, contextSpecificSettingsString);
}
}
@@ -3,8 +3,9 @@ package com.sap.sse.security.ui.settings;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
import com.sap.sse.gwt.client.shared.settings.ComponentContextWithSettingsStorage;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.gwt.client.shared.settings.SettingsSerializationHelper;
import com.sap.sse.security.ui.client.UserService;
/**
* Adds settings patching functionality to {@link ComponentContextWithSettingsStorage} implementation.
@@ -26,20 +27,24 @@ import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
*/
public class ComponentContextWithSettingsStorageAndPatching<S extends Settings> extends ComponentContextWithSettingsStorage<S> {
/**
* Manages the persistence layer of settings.
*/
protected final UserSettingsStorageManagerWithPatching<S> settingsStorageManager;
/**
* @param rootLifecycle
* The {@link ComponentLifecycle} of the root component/perspective
* @param settingsStorageManager
* The {@link UserSettingsStorageManagerWithPatching} to be used to access stored settings and to store new settings
* @param userService
* The service which is used for server-side settings storage
* @param storageDefinitionId
* The definition for User Settings and Document Settings storage keys
*/
public ComponentContextWithSettingsStorageAndPatching(ComponentLifecycle<S> rootLifecycle, UserSettingsStorageManagerWithPatching<S> settingsStorageManager) {
super(rootLifecycle, settingsStorageManager);
this.settingsStorageManager = (UserSettingsStorageManagerWithPatching<S>) super.settingsStorageManager;
public ComponentContextWithSettingsStorageAndPatching(ComponentLifecycle<S> rootLifecycle, UserService userService, StorageDefinitionId storageDefinitionId) {
this(rootLifecycle, userService, storageDefinitionId, new SettingsSerializationHelper());
}
protected ComponentContextWithSettingsStorageAndPatching(ComponentLifecycle<S> rootLifecycle, UserService userService, StorageDefinitionId storageDefinitionId, SettingsSerializationHelper settingsSerializationHelper) {
this(rootLifecycle, userService, storageDefinitionId, settingsSerializationHelper, new UserSettingsBuildingPipelineWithPatching(settingsSerializationHelper));
}
protected ComponentContextWithSettingsStorageAndPatching(ComponentLifecycle<S> rootLifecycle, UserService userService, StorageDefinitionId storageDefinitionId, SettingsSerializationHelper settingsSerializationHelper, UserSettingsBuildingPipelineWithPatching settingsBuildingPipeline) {
super(rootLifecycle, userService, storageDefinitionId, settingsSerializationHelper, settingsBuildingPipeline);
}
/**
@@ -50,7 +55,7 @@ public class ComponentContextWithSettingsStorageAndPatching<S extends Settings>
* @param settingsPatch The settings patch to apply on settings
*/
public<CS extends Settings> void addPatchForStoringSettings(Component<CS> component, PipelineLevel pipelineLevel, SettingsPatch<CS> settingsPatch) {
settingsStorageManager.addPatchForStoringSettings(component, pipelineLevel, settingsPatch);
((UserSettingsBuildingPipelineWithPatching) settingsBuildingPipeline).addPatchForStoringSettings(component, pipelineLevel, settingsPatch);
}
/**
@@ -61,8 +66,7 @@ public class ComponentContextWithSettingsStorageAndPatching<S extends Settings>
* @param settingsPatch The settings patch to apply on settings
*/
public<CS extends Settings> void addPatchForLoadingSettings(Component<CS> component, PipelineLevel pipelineLevel, SettingsPatch<CS> settingsPatch) {
invalidateCachedSettings();
settingsStorageManager.addPatchForLoadingSettings(component, pipelineLevel, settingsPatch);
((UserSettingsBuildingPipelineWithPatching) settingsBuildingPipeline).addPatchForLoadingSettings(component, pipelineLevel, settingsPatch);
}
}
@@ -0,0 +1,75 @@
package com.sap.sse.security.ui.settings;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.common.settings.generic.GenericSerializableSettings;
import com.sap.sse.common.settings.generic.SettingsMap;
import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
import com.sap.sse.gwt.client.shared.settings.SettingsBuildingPipeline;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsSerializationHelper;
import com.sap.sse.gwt.settings.SettingsToStringSerializer;
import com.sap.sse.security.ui.client.UserService;
/**
* Specialization of {@link ComponentContextWithSettingsStorage} which reimplements URL deserialization. This implementation
* handles URL deserialization in such a way, that only the provided {@code serializedSettings} {@code String} is
* considered as URL, and not the URL in the browser.
*
* @author Vladislav Chumak
*
* @see ComponentContextWithSettingsStorage
*
*/
public class PlaceBasedComponentContextWithSettingsStorage<S extends Settings> extends ComponentContextWithSettingsStorage<S> {
/**
* Constructs a special case of {@link ComponentContextWithSettingsStorage} which considers the provided {@code serializedSettings} as
* URL parameters for deserialization of URL Settings.
*
* @param rootLifecycle
* The {@link ComponentLifecycle} of the root component/perspective
* @param userService
* The service which is used for server-side settings storage
* @param storageDefinitionId
* The definition for User Settings and Document Settings storage keys
* @param serializedSettings
* Contains URL parameters for serialization of URL Settings
*/
public PlaceBasedComponentContextWithSettingsStorage(ComponentLifecycle<S> rootLifecycle, UserService userService,
StorageDefinitionId storageDefinitionId, final String serializedSettings) {
this(rootLifecycle, userService, storageDefinitionId, new SettingsSerializationHelper() {
private final SettingsToStringSerializer settingsToStringSerializer = new SettingsToStringSerializer();
@SuppressWarnings("unchecked")
@Override
public <CS extends Settings> CS deserializeFromCurrentUrl(CS defaultSettings) {
final CS result;
if (defaultSettings instanceof SettingsMap) {
result = (CS) settingsToStringSerializer.fromString(serializedSettings,
(SettingsMap) defaultSettings);
} else if (defaultSettings instanceof GenericSerializableSettings) {
result = (CS) settingsToStringSerializer.fromString(serializedSettings,
(GenericSerializableSettings) defaultSettings);
} else {
result = defaultSettings;
}
return result;
}
});
}
protected PlaceBasedComponentContextWithSettingsStorage(ComponentLifecycle<S> rootLifecycle, UserService userService,
StorageDefinitionId storageDefinitionId, SettingsSerializationHelper settingsSerializationHelper) {
this(rootLifecycle, userService, storageDefinitionId, settingsSerializationHelper,
new UserSettingsBuildingPipeline(settingsSerializationHelper));
}
protected PlaceBasedComponentContextWithSettingsStorage(ComponentLifecycle<S> rootLifecycle, UserService userService,
StorageDefinitionId storageDefinitionId, SettingsSerializationHelper settingsSerializationHelper,
SettingsBuildingPipeline<SettingsJsons> settingsBuildingPipeline) {
super(rootLifecycle, userService, storageDefinitionId, settingsSerializationHelper, settingsBuildingPipeline);
}
}
@@ -1,50 +0,0 @@
package com.sap.sse.security.ui.settings;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.common.settings.generic.GenericSerializableSettings;
import com.sap.sse.common.settings.generic.SettingsMap;
import com.sap.sse.gwt.client.shared.settings.SettingsStringConverter;
import com.sap.sse.gwt.settings.SettingsToStringSerializer;
/**
* Specialization of {@link UserSettingsBuildingPipeline} which reimplements URL deserialization.
* This implementation handles URL deserialization in such a way, that only the provided
* {@code serializedSettings} {@code String} is considered as URL, and not the URL in the browser.
*
* @author Vladislav Chumak
*
* @see UserSettingsBuildingPipeline
*
*/
public class PlaceBasedUserSettingsBuildingPipeline extends UserSettingsBuildingPipeline {
/**
* Constructs an instance with a custom conversion helper which considers the provided {@code serializedSettings}
* as URL parameters for deserialization of URL Settings.
*
* @param serializedSettings
*/
public PlaceBasedUserSettingsBuildingPipeline(final String serializedSettings) {
super(new SettingsStringConverter() {
private final SettingsToStringSerializer settingsToStringSerializer = new SettingsToStringSerializer();
@SuppressWarnings("unchecked")
@Override
public <S extends Settings> S deserializeFromCurrentUrl(S defaultSettings) {
final S result;
if (defaultSettings instanceof SettingsMap) {
result = (S) settingsToStringSerializer.fromString(serializedSettings, (SettingsMap) defaultSettings);
} else if (defaultSettings instanceof GenericSerializableSettings) {
result = (S) settingsToStringSerializer.fromString(serializedSettings,
(GenericSerializableSettings) defaultSettings);
} else {
result = defaultSettings;
}
return result;
}
});
}
}
@@ -1,21 +0,0 @@
package com.sap.sse.security.ui.settings;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.security.ui.client.UserService;
/**
* Specialization of {@link UserSettingsStorageManager} which uses {@link PlaceBasedUserSettingsBuildingPipeline} for settings construction.
*
* @author Vladislav Chumak
*
* @param <S>
* The {@link Settings} type of the settings of the root component/perspective containing all the settings
* for itself and its subcomponents
*/
public class PlaceBasedUserSettingsStorageManager<S extends Settings> extends UserSettingsStorageManager<S> {
public PlaceBasedUserSettingsStorageManager(UserService userService, StorageDefinitionId storageDefinitionId, String serializedSettings) {
super(userService, storageDefinitionId, new PlaceBasedUserSettingsBuildingPipeline(serializedSettings));
}
}
@@ -1,16 +1,22 @@
package com.sap.sse.gwt.client.shared.settings;
package com.sap.sse.security.ui.settings;
import java.util.ArrayList;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.client.shared.settings.ComponentUtils;
import com.sap.sse.gwt.client.shared.settings.OnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.OnSettingsStoredCallback;
import com.sap.sse.gwt.client.shared.settings.SettingsBuildingPipeline;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsSerializationHelper;
/**
* Manages all default settings of perspectives and components. This simple implementation has no support for settings
* storage. It is only capable of creating new default settings by means of {@link ComponentLifecycle#createDefaultSettings()} of the root
* component managed by this {@link ComponentContext}. All in all, it is a dummy implementation for components which does
* not have support for settings storage. If you need settings to be stored, consider
* component managed by this {@link ComponentContext}, considering the URL parameters. If you need settings to be stored, consider
* {@link ComponentContextWithSettingsStorage}.
*
* @author Vladislav Chumak
@@ -20,6 +26,13 @@ import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
* for itself and its subcomponents
*/
public class SimpleComponentContext<S extends Settings> implements ComponentContext<S> {
protected final SettingsSerializationHelper settingsSerializationHelper;
/**
* The pipeline used for the settings construction.
*/
protected final SettingsBuildingPipeline<SettingsJsons> settingsBuildingPipeline;
/**
* The {@link ComponentLifecycle} of the root component/perspective
@@ -32,7 +45,17 @@ public class SimpleComponentContext<S extends Settings> implements ComponentCont
* The {@link ComponentLifecycle} of the root component/perspective
*/
public SimpleComponentContext(ComponentLifecycle<S> rootLifecycle) {
this(rootLifecycle, new SettingsSerializationHelper());
}
protected SimpleComponentContext(ComponentLifecycle<S> rootLifecycle, SettingsSerializationHelper settingsSerializationHelper) {
this(rootLifecycle, settingsSerializationHelper, new UrlSettingsBuildingPipeline(settingsSerializationHelper));
}
protected SimpleComponentContext(ComponentLifecycle<S> rootLifecycle, SettingsSerializationHelper settingsSerializationHelper, SettingsBuildingPipeline<SettingsJsons> settingsBuildingPipeline) {
this.rootLifecycle = rootLifecycle;
this.settingsSerializationHelper = settingsSerializationHelper;
this.settingsBuildingPipeline = settingsBuildingPipeline;
}
/**
@@ -70,15 +93,16 @@ public class SimpleComponentContext<S extends Settings> implements ComponentCont
*
* @return The {@link Settings} of the root component
*/
public S getDefaultSettings() {
return rootLifecycle.createDefaultSettings();
private S getDefaultSettings() {
S defaultSettings = rootLifecycle.createDefaultSettings();
return settingsBuildingPipeline.getSettingsObject(defaultSettings, new SettingsJsons(null, null));
}
/**
* This method returns always {@code false}, because it does not offer functionality for settings storage.
*/
@Override
public boolean hasMakeCustomDefaultSettingsSupport(Component<?> component) {
public boolean isStorageSupported(Component<?> component) {
return false;
}
@@ -101,14 +125,25 @@ public class SimpleComponentContext<S extends Settings> implements ComponentCont
}
/**
* Retrieves the "System Default" {@link Settings} of the provided component and passes them to the provided callback.
*
* @see #getDefaultSettings()
* {@inheritDoc}
*/
@Override
public <CS extends Settings> void getInitialSettingsForComponent(final Component<CS> component, final OnSettingsLoadedCallback<CS> callback) {
ComponentLifecycle<CS> componentLifecycle = ComponentUtils.determineLifecycle(new ArrayList<>(component.getPath()), rootLifecycle);
callback.onSuccess(componentLifecycle.createDefaultSettings());
}
OnSettingsLoadedCallback<S> internalCallback = new OnSettingsLoadedCallback<S>() {
@Override
public void onError(Throwable caught, S fallbackDefaultSettings) {
CS componentFallbackSettings = ComponentUtils.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(component.getPath()), fallbackDefaultSettings);
callback.onError(caught, componentFallbackSettings);
}
@Override
public void onSuccess(S settings) {
CS componentSettings = ComponentUtils.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(component.getPath()), settings);
callback.onSuccess(componentSettings);
}
};
getInitialSettings(internalCallback);
}
}
@@ -1,92 +0,0 @@
package com.sap.sse.security.ui.settings;
import java.util.List;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.shared.settings.OnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.OnSettingsStoredCallback;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
/**
* This {@link SettingsStorageManager} implementation only reads settings from the URL and does not support settings storage.
*
* @param <S>
* The {@link Settings} type of the settings of the root component/perspective containing all the settings
* for itself and its subcomponents
* @see SettingsStorageManager
*/
public class SimpleSettingsStorageManager<S extends Settings> implements SettingsStorageManager<S> {
/**
* The pipeline used for the settings construction.
*/
protected final UrlSettingsBuildingPipeline settingsBuildingPipeline;
/**
* Constructs the instance with a {@link UrlSettingsBuildingPipeline}.
*/
public SimpleSettingsStorageManager() {
settingsBuildingPipeline = new UrlSettingsBuildingPipeline();
}
/**
* {@inheritDoc}
*/
@Override
public boolean supportsStore() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void retrieveDefaultSettings(S defaultSettings, final OnSettingsLoadedCallback<S> asyncCallback) {
asyncCallback.onSuccess(settingsBuildingPipeline.getSettingsObject(defaultSettings));
}
/**
* {@inheritDoc}
*/
@Override
public void storeGlobalSettingsJson(JSONObject globalSettingsJson, OnSettingsStoredCallback onSettingsStoredCallback) {
throw new UnsupportedOperationException("Settings storage is unsupported by SimpleSettingsStorageManager");
}
/**
* {@inheritDoc}
*/
@Override
public void storeContextSpecificSettingsJson(JSONObject contextSpecificSettingsJson,
OnSettingsStoredCallback onSettingsStoredCallback) {
throw new UnsupportedOperationException("Settings storage is unsupported by SimpleSettingsStorageManager");
}
/**
* {@inheritDoc}
*/
@Override
public JSONValue settingsToJSON(Settings newSettings, PipelineLevel pipelineLevel, List<String> path) {
throw new UnsupportedOperationException("Settings storage is unsupported by SimpleSettingsStorageManager");
}
/**
* {@inheritDoc}
*/
@Override
public void retrieveSettingsJsons(AsyncCallback<SettingsJsons> asyncCallback) {
throw new UnsupportedOperationException("Settings storage is unsupported by SimpleSettingsStorageManager");
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
}
}
@@ -4,41 +4,60 @@ import java.util.List;
import com.google.gwt.json.client.JSONValue;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.shared.settings.AbstractSettingsBuildingPipeline;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.gwt.client.shared.settings.SettingsBuildingPipeline;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsSerializationHelper;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
/**
* Settings building pipeline which is only capable of reading settings from URL.
* Conversion to JSON is not supported. This implementation is supposed to be used by {@link SettingsStorageManager}
* which offers read-only functionality for settings, and thus, does not provide persistence support.
* Settings building pipeline which is only capable of reading settings from URL. Conversion to JSON is not supported.
* This implementation is supposed to be used by {@link SettingsStorageManager} which offers read-only functionality for
* settings, and thus, does not provide persistence support.
*
*
* @author Vladislav Chumak
*
*/
public class UrlSettingsBuildingPipeline extends AbstractSettingsBuildingPipeline {
public class UrlSettingsBuildingPipeline implements SettingsBuildingPipeline<SettingsJsons> {
/**
* Constructs a settings object by means of provided defaultSettings and current URL.
*
* @param defaultSettings The basic settings to be used
* @param settingsJsons The persisted representation of User Settings and Document Settings, which is ignored by this implementation
* @return The constructed settings object
* Conversion helper which is used by this instance for type conversion/serialization between settings objects and
* JSON Strings.
*/
@Override
public <S extends Settings> S getSettingsObject(S defaultSettings, SettingsJsons settingsJsons) {
return settingsStringConverter.deserializeFromCurrentUrl(defaultSettings);
protected final SettingsSerializationHelper settingsSerializationHelper;
/**
* Constructs an instance with a custom conversion helper between settings objects and its JSON representation.
*
* @param settingsSerializationHelper
* The custom conversion helper
*/
public UrlSettingsBuildingPipeline(SettingsSerializationHelper settingsSerializationHelper) {
this.settingsSerializationHelper = settingsSerializationHelper;
}
/**
* This implementation does not provide support for JSON conversion of settings objects, because it is supposed to be used
* by read-only {@link SettingsStorageManager} implementations.
* Constructs a settings object by means of provided defaultSettings and current URL.
*
* @param defaultSettings
* The basic settings to be used
* @param settingsRepresentation
* The persisted representation of Settings, which is ignored by this implementation
* @return The constructed settings object
*/
@Override
public <S extends Settings> S getSettingsObject(S defaultSettings, SettingsJsons settingsRepresentation) {
return settingsSerializationHelper.deserializeFromCurrentUrl(defaultSettings);
}
/**
* This implementation does not provide support for JSON conversion of settings objects, because it is supposed to
* be used by read-only {@link SettingsStorageManager} implementations.
*/
@Override
public JSONValue getJsonObject(Settings settings, PipelineLevel pipelineLevel, List<String> path) {
throw new UnsupportedOperationException("This pipeline does not support JSON conversion");
}
}
@@ -4,72 +4,80 @@ import java.util.List;
import com.google.gwt.json.client.JSONValue;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.shared.settings.AbstractSettingsBuildingPipeline;
import com.sap.sse.gwt.client.shared.perspective.IgnoreLocalSettings;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsStringConverter;
import com.sap.sse.gwt.client.shared.settings.SettingsSerializationHelper;
/**
* Settings building pipeline which is capable of building settings considering
* System Default Settings, persisted representations of
* User Settings and Document Settings, and current URL.
* The precedence of settings is:
* Settings building pipeline which is capable of building settings considering System Default Settings, persisted
* representations of User Settings and Document Settings, and current URL. The precedence of settings is:
* <ul>
* <li>System Default Settings</li>
* <li>User Settings</li>
* <li>Document Settings</li>
* <li>URL Settings</li>
* <li>System Default Settings</li>
* <li>User Settings</li>
* <li>Document Settings</li>
* <li>URL Settings</li>
* </ul>
*
* @author Vladislav Chumak
*
*/
public class UserSettingsBuildingPipeline extends AbstractSettingsBuildingPipeline {
public class UserSettingsBuildingPipeline extends UrlSettingsBuildingPipeline {
/**
* Constructs an instance with a custom conversion helper between
* settings objects and its JSON representation.
* Constructs an instance with a custom conversion helper between settings objects and its JSON representation.
*
* @param settingsSerializationHelper
* The custom conversion helper
*/
protected UserSettingsBuildingPipeline(SettingsStringConverter settingsStringConverter) {
public UserSettingsBuildingPipeline(SettingsSerializationHelper settingsStringConverter) {
super(settingsStringConverter);
}
/**
* Constructs an instance with {@link SettingsStringConverter} as conversion helper between
* settings objects and its JSON representation.
*/
public UserSettingsBuildingPipeline() {
}
/**
* Constructs a settings object by means of provided defaultSettings, persisted representations of
* User Settings and Document Settings, and current URL.
* Constructs a settings object by means of provided defaultSettings, persisted representations of User Settings and
* Document Settings, and current URL.
*
* @param defaultSettings The basic settings to be used
* @param settingsJsons The persisted representation of User Settings and Document Settings
* @param defaultSettings
* The basic settings to be used
* @param settingsJsons
* The persisted representation of User Settings and Document Settings
* @return The constructed settings object
*/
public<S extends Settings> S getSettingsObject(S defaultSettings, SettingsJsons settingsJsons) {
if(settingsJsons.getContextSpecificSettingsJson() != null) {
defaultSettings = settingsStringConverter.deserializeFromJson(defaultSettings, settingsJsons.getContextSpecificSettingsJson());
} else if(settingsJsons.getGlobalSettingsJson() != null) {
defaultSettings = settingsStringConverter.deserializeFromJson(defaultSettings, settingsJsons.getGlobalSettingsJson());
@Override
public <CS extends Settings> CS getSettingsObject(CS defaultSettings, SettingsJsons settingsJsons) {
if (!isIgnoreLocalSettingsUrlFlagPresent()) {
if (settingsJsons.getContextSpecificSettingsJson() != null) {
defaultSettings = settingsSerializationHelper.deserializeFromJson(defaultSettings,
settingsJsons.getContextSpecificSettingsJson());
} else if (settingsJsons.getGlobalSettingsJson() != null) {
defaultSettings = settingsSerializationHelper.deserializeFromJson(defaultSettings,
settingsJsons.getGlobalSettingsJson());
}
}
defaultSettings = settingsStringConverter.deserializeFromCurrentUrl(defaultSettings);
defaultSettings = settingsSerializationHelper.deserializeFromCurrentUrl(defaultSettings);
return defaultSettings;
}
/**
* Converts the provided settings object into a JSON representation without considering provided pipeline level and settings tree path.
* Converts the provided settings object into a JSON representation without considering provided pipeline level and
* settings tree path.
*
* @param settings The settings to convert to JSON representation
* @param pipelineLevel The pipeline level which indicates the storage scope, e.g. User Settings or Document Settings.
* @param path The path of the settings in the settings tree
* @param settings
* The settings to convert to JSON representation
* @param pipelineLevel
* The pipeline level which indicates the storage scope, e.g. User Settings or Document Settings.
* @param path
* The path of the settings in the settings tree
* @return The JSON representation of the provided settings
*/
@Override
public JSONValue getJsonObject(Settings newSettings, PipelineLevel pipelineLevel, List<String> path) {
return settingsStringConverter.serializeFromSettingsObject(newSettings);
return settingsSerializationHelper.serializeFromSettingsObject(newSettings);
}
public boolean isIgnoreLocalSettingsUrlFlagPresent() {
return settingsSerializationHelper.deserializeFromCurrentUrl(new IgnoreLocalSettings()).isIgnoreLocalSettings();
}
}
@@ -13,78 +13,100 @@ import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.settings.ComponentUtils;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsSerializationHelper;
/**
* Specialization of {@link UserSettingsBuildingPipeline} which offers multiple hooks for settings patching
* throughout the pipeline. These hooks may be used to patch the settings at different pipeline levels during its construction.
* This implementation provides additional methods for attachment of {@link SettingsPatch}s.
* After each {@link PipelineLevel} a custom {@link SettingsPatch}
* may be applied. The patch may partially or completely modify the resulting settings object in order to provide
* the desired behavior of default settings for a dynamic environment, e.g. RaceBoard which determines its default settings regarding
* to RaceModes, PlayModes and etc.
* Specialization of {@link UserSettingsBuildingPipeline} which offers multiple hooks for settings patching throughout
* the pipeline. These hooks may be used to patch the settings at different pipeline levels during its construction.
* This implementation provides additional methods for attachment of {@link SettingsPatch}s. After each
* {@link PipelineLevel} a custom {@link SettingsPatch} may be applied. The patch may partially or completely modify the
* resulting settings object in order to provide the desired behavior of default settings for a dynamic environment,
* e.g. RaceBoard which determines its default settings regarding to RaceModes, PlayModes and etc.
*
* @author Vladislav Chumak
*
*/
public class UserSettingsBuildingPipelineWithPatching extends UserSettingsBuildingPipeline {
private SettingsPatches patchesForStoringSettings = new SettingsPatches();
private SettingsPatches patchesForLoadingSettings = new SettingsPatches();
/**
* Constructs a settings object by means of provided defaultSettings, persisted representations of
* User Settings and Document Settings, current URL, and loading patches, which have been added to this
* pipeline instance.
* Constructs an instance with a custom conversion helper between settings objects and its JSON representation.
*
* @param defaultSettings The basic settings to be used
* @param settingsJsons The persisted representation of User Settings and Document Settings
* @param settingsSerializationHelper
* The custom conversion helper
*/
public UserSettingsBuildingPipelineWithPatching(SettingsSerializationHelper settingsStringConverter) {
super(settingsStringConverter);
}
/**
* Constructs a settings object by means of provided defaultSettings, persisted representations of User Settings and
* Document Settings, current URL, and loading patches, which have been added to this pipeline instance.
*
* @param defaultSettings
* The basic settings to be used
* @param settingsJsons
* The persisted representation of User Settings and Document Settings
* @return The constructed settings object
*/
@Override
public <CS extends Settings> CS getSettingsObject(CS defaultSettings, SettingsJsons settingsJsons) {
boolean ignoreLocalSettings = isIgnoreLocalSettingsUrlFlagPresent();
defaultSettings = applyPatchesForPipelineLevel(defaultSettings, PipelineLevel.SYSTEM_DEFAULTS);
if(settingsJsons.getContextSpecificSettingsJson() != null) {
if (!ignoreLocalSettings && settingsJsons.getContextSpecificSettingsJson() != null) {
defaultSettings = applyPatchesForPipelineLevel(defaultSettings, PipelineLevel.GLOBAL_DEFAULTS);
defaultSettings = settingsStringConverter.deserializeFromJson(defaultSettings, settingsJsons.getContextSpecificSettingsJson());
} else if(settingsJsons.getGlobalSettingsJson() != null) {
defaultSettings = settingsStringConverter.deserializeFromJson(defaultSettings, settingsJsons.getGlobalSettingsJson());
defaultSettings = settingsSerializationHelper.deserializeFromJson(defaultSettings,
settingsJsons.getContextSpecificSettingsJson());
} else if (!ignoreLocalSettings && settingsJsons.getGlobalSettingsJson() != null) {
defaultSettings = settingsSerializationHelper.deserializeFromJson(defaultSettings,
settingsJsons.getGlobalSettingsJson());
defaultSettings = applyPatchesForPipelineLevel(defaultSettings, PipelineLevel.GLOBAL_DEFAULTS);
} else {
defaultSettings = applyPatchesForPipelineLevel(defaultSettings, PipelineLevel.GLOBAL_DEFAULTS);
}
defaultSettings = applyPatchesForPipelineLevel(defaultSettings, PipelineLevel.CONTEXT_SPECIFIC_DEFAULTS);
defaultSettings = settingsStringConverter.deserializeFromCurrentUrl(defaultSettings);
defaultSettings = settingsSerializationHelper.deserializeFromCurrentUrl(defaultSettings);
return defaultSettings;
}
private <CS extends Settings> CS applyPatchesForPipelineLevel(CS defaultSettings, PipelineLevel pipelineLevel) {
for (Entry<List<String>, List<SettingsPatch<? extends Settings>>> entry : patchesForLoadingSettings.getSettingsPatches(pipelineLevel).entrySet()) {
for (Entry<List<String>, List<SettingsPatch<? extends Settings>>> entry : patchesForLoadingSettings
.getSettingsPatches(pipelineLevel).entrySet()) {
List<String> path = entry.getKey();
List<SettingsPatch<? extends Settings>> settingsPatches = entry.getValue();
if(!settingsPatches.isEmpty()) {
Settings patchedComponentSettings = ComponentUtils.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(path), defaultSettings);
if (!settingsPatches.isEmpty()) {
Settings patchedComponentSettings = ComponentUtils
.determineComponentSettingsFromPerspectiveSettings(new ArrayList<>(path), defaultSettings);
for (SettingsPatch<? extends Settings> settingsPatch : settingsPatches) {
patchedComponentSettings = patchSettings(patchedComponentSettings, settingsPatch);
}
defaultSettings = ComponentUtils.patchSettingsTree(new ArrayList<>(path), patchedComponentSettings, defaultSettings);
defaultSettings = ComponentUtils.patchSettingsTree(new ArrayList<>(path), patchedComponentSettings,
defaultSettings);
}
}
return defaultSettings;
}
/**
* Converts the provided settings object into a JSON representation considering storing patches,
* which have been added to this pipeline instance.
* Converts the provided settings object into a JSON representation considering storing patches, which have been
* added to this pipeline instance.
*
* @param settings The settings to convert to JSON representation
* @param pipelineLevel The pipeline level which indicates the storage scope, e.g. User Settings or Document Settings.
* @param path The path of the settings in the settings tree
* @param settings
* The settings to convert to JSON representation
* @param pipelineLevel
* The pipeline level which indicates the storage scope, e.g. User Settings or Document Settings.
* @param path
* The path of the settings in the settings tree
* @return The JSON representation of the provided settings
*/
@Override
public JSONValue getJsonObject(Settings settings, PipelineLevel pipelineLevel, List<String> path) {
for (PipelineLevel level : pipelineLevel.getSortedLevelsUntilCurrent()) {
List<SettingsPatch<? extends Settings>> settingsPatches = patchesForStoringSettings.getSettingsPatches(path, level);
List<SettingsPatch<? extends Settings>> settingsPatches = patchesForStoringSettings.getSettingsPatches(path,
level);
for (SettingsPatch<? extends Settings> settingsPatch : settingsPatches) {
settings = patchSettings(settings, settingsPatch);
}
@@ -93,76 +115,90 @@ public class UserSettingsBuildingPipelineWithPatching extends UserSettingsBuildi
}
@SuppressWarnings("unchecked")
private<CS extends Settings> CS patchSettings(Settings settings, SettingsPatch<CS> settingsPatch) {
private <CS extends Settings> CS patchSettings(Settings settings, SettingsPatch<CS> settingsPatch) {
return settingsPatch.patchSettings((CS) settings);
}
/**
* Adds a settings patch for transforming settings before storing them.
*
* @param component The component which the targeted settings for patching belong to
* @param pipelineLevel The pipeline level <b>AFTER</b> that the patch should be applied on settings
* @param settingsPatch The settings patch to apply on settings
* @param component
* The component which the targeted settings for patching belong to
* @param pipelineLevel
* The pipeline level <b>AFTER</b> that the patch should be applied on settings
* @param settingsPatch
* The settings patch to apply on settings
*/
public<CS extends Settings> void addPatchForStoringSettings(Component<CS> component, PipelineLevel pipelineLevel, SettingsPatch<CS> settingsPatch) {
public <CS extends Settings> void addPatchForStoringSettings(Component<CS> component, PipelineLevel pipelineLevel,
SettingsPatch<CS> settingsPatch) {
patchesForStoringSettings.addSettingsPatch(component.getPath(), settingsPatch, pipelineLevel);
}
/**
* Adds a settings patch for settings object construction.
*
* @param component The component which the targeted settings for patching belong to
* @param pipelineLevel The pipeline level <b>AFTER</b> that the patch should be applied on settings
* @param settingsPatch The settings patch to apply on settings
* @param component
* The component which the targeted settings for patching belong to
* @param pipelineLevel
* The pipeline level <b>AFTER</b> that the patch should be applied on settings
* @param settingsPatch
* The settings patch to apply on settings
*/
public<CS extends Settings> void addPatchForLoadingSettings(Component<CS> component, PipelineLevel pipelineLevel, SettingsPatch<CS> settingsPatch) {
public <CS extends Settings> void addPatchForLoadingSettings(Component<CS> component, PipelineLevel pipelineLevel,
SettingsPatch<CS> settingsPatch) {
patchesForLoadingSettings.addSettingsPatch(component.getPath(), settingsPatch, pipelineLevel);
}
/**
* Internal helper class for management of provided settings patches in relation to its pipeline levels
* and components that provided patches belong to.
* Internal helper class for management of provided settings patches in relation to its pipeline levels and
* components that provided patches belong to.
*
* @author Vladislav Chumak
*
*/
private static class SettingsPatches {
private Map<PipelineLevel, Map<List<String>, List<SettingsPatch<? extends Settings>>>> patchesForSettings = new HashMap<>();
public void addSettingsPatch(List<String> path, SettingsPatch<? extends Settings> settingsPatch, PipelineLevel pipelineLevel) {
Map<List<String>, List<SettingsPatch<? extends Settings>>> pipelinePatches = patchesForSettings.get(pipelineLevel);
if(pipelinePatches == null) {
public void addSettingsPatch(List<String> path, SettingsPatch<? extends Settings> settingsPatch,
PipelineLevel pipelineLevel) {
Map<List<String>, List<SettingsPatch<? extends Settings>>> pipelinePatches = patchesForSettings
.get(pipelineLevel);
if (pipelinePatches == null) {
pipelinePatches = new HashMap<>();
patchesForSettings.put(pipelineLevel, pipelinePatches);
}
List<SettingsPatch<? extends Settings>> componentPatches = pipelinePatches.get(path);
if(componentPatches == null) {
if (componentPatches == null) {
componentPatches = new ArrayList<>();
pipelinePatches.put(path, componentPatches);
}
componentPatches.add(settingsPatch);
}
public List<SettingsPatch<? extends Settings>> getSettingsPatches(List<String> path, PipelineLevel pipelineLevel) {
Map<List<String>, List<SettingsPatch<? extends Settings>>> pipelinePatches = patchesForSettings.get(pipelineLevel);
if(pipelinePatches == null) {
public List<SettingsPatch<? extends Settings>> getSettingsPatches(List<String> path,
PipelineLevel pipelineLevel) {
Map<List<String>, List<SettingsPatch<? extends Settings>>> pipelinePatches = patchesForSettings
.get(pipelineLevel);
if (pipelinePatches == null) {
return Collections.emptyList();
}
List<SettingsPatch<? extends Settings>> componentPatches = pipelinePatches.get(path);
if(componentPatches == null) {
if (componentPatches == null) {
return Collections.emptyList();
}
return componentPatches;
}
public Map<List<String>, List<SettingsPatch<? extends Settings>>> getSettingsPatches(PipelineLevel pipelineLevel) {
Map<List<String>, List<SettingsPatch<? extends Settings>>> pipelinePatches = patchesForSettings.get(pipelineLevel);
if(pipelinePatches == null) {
public Map<List<String>, List<SettingsPatch<? extends Settings>>> getSettingsPatches(
PipelineLevel pipelineLevel) {
Map<List<String>, List<SettingsPatch<? extends Settings>>> pipelinePatches = patchesForSettings
.get(pipelineLevel);
if (pipelinePatches == null) {
return Collections.emptyMap();
}
return pipelinePatches;
}
}
}
@@ -7,20 +7,12 @@ import java.util.List;
import java.util.Map;
import java.util.Queue;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.storage.client.Storage;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.shared.perspective.IgnoreLocalSettings;
import com.sap.sse.gwt.client.shared.settings.OnSettingsLoadedCallback;
import com.sap.sse.gwt.client.shared.settings.OnSettingsStoredCallback;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.gwt.client.shared.settings.SettingsBuildingPipeline;
import com.sap.sse.gwt.client.shared.settings.SettingsJsons;
import com.sap.sse.gwt.client.shared.settings.SettingsStorageManager;
import com.sap.sse.gwt.client.shared.settings.SettingsStrings;
import com.sap.sse.gwt.settings.SettingsToUrlSerializer;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.client.UserStatusEventHandler;
import com.sap.sse.security.ui.shared.UserDTO;
@@ -31,23 +23,10 @@ import com.sap.sse.security.ui.shared.UserDTO;
*
* @author Vladislav Chumak
*
* @param <S>
* The {@link Settings} type of the settings of the root component/perspective containing all the settings
* for itself and its subcomponents
* @see SettingsStorageManager
*/
public class UserSettingsStorageManager<S extends Settings> implements SettingsStorageManager<S> {
public class UserSettingsStorageManager implements SettingsStorageManager<SettingsStrings> {
/**
* {@code SettingsJsons} which have which have been already queried from persistence layer, or {@code null} if previous settings retrievement has not been initiated.
*/
protected SettingsJsons cachedSettingsJsons = null;
/**
* The pipeline used for the settings construction.
*/
protected final SettingsBuildingPipeline settingsBuildingPipeline;
/**
* The key which is associated with the User Settings. Different keys will cause multiple/different settings
* instances to be stored in the storage.
@@ -78,109 +57,76 @@ public class UserSettingsStorageManager<S extends Settings> implements SettingsS
/**
* Callbacks which are waiting to be called when persistent settings representation has loaded.
*/
private Queue<AsyncCallback<SettingsJsons>> retrieveSettingsCallbacksQueue = new LinkedList<>();
private Queue<OnSettingsLoadedCallback<SettingsStrings>> retrieveSettingsCallbacksQueue = new LinkedList<>();
/**
* Constructs the instance with a custom {@link SettingsBuildingPipeline}.
*
* @param userService The service which is used for server-side settings storage
* @param storageDefinitionId The definition for User Settings and Document Settings storage keys
* @param settingsBuildingPipeline The custom pipeline to be used for settings construction
*/
protected UserSettingsStorageManager(UserService userService, StorageDefinitionId storageDefinitionId, SettingsBuildingPipeline settingsBuildingPipeline) {
this.settingsBuildingPipeline = settingsBuildingPipeline;
public UserSettingsStorageManager(UserService userService, StorageDefinitionId storageDefinitionId) {
this.userService = userService;
this.storageGlobalKey = storageDefinitionId.generateStorageGlobalKey();
this.storageContextSpecificKey = storageDefinitionId.generateStorageContextSpecificKey();
}
/**
* Constructs the instance with {@link UserSettingsBuildingPipeline}.
*
* @param userService The service which is used for server-side settings storage
* @param storageDefinitionId The definition for User Settings and Document Settings storage keys
*/
public UserSettingsStorageManager(UserService userService, StorageDefinitionId storageDefinitionId) {
this(userService, storageDefinitionId, new UserSettingsBuildingPipeline());
@Override
public void storeSettingsRepresentation(SettingsStrings settingsStrings, OnSettingsStoredCallback onSettingsStoredCallback) {
storeSettingsStringsOnLocalStorage(settingsStrings);
if (userService.getCurrentUser() != null) {
storeSettingsStringsOnServer(settingsStrings, onSettingsStoredCallback);
} else {
onSettingsStoredCallback.onSuccess();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean supportsStore() {
return true;
}
private void storeSettingsStringsOnLocalStorage(SettingsStrings settingsStrings, boolean storeGlobalSettings, boolean storeContextSpecificSettings) {
private void storeSettingsStringsOnLocalStorage(SettingsStrings settingsStrings) {
Storage localStorage = Storage.getLocalStorageIfSupported();
if (localStorage != null) {
if(storeGlobalSettings) {
if(settingsStrings.getGlobalSettingsString() != null) {
localStorage.removeItem(storageGlobalKey);
if(settingsStrings.getGlobalSettingsString() != null) {
localStorage.setItem(storageGlobalKey, settingsStrings.getGlobalSettingsString());
}
localStorage.setItem(storageGlobalKey, settingsStrings.getGlobalSettingsString());
}
if(storeContextSpecificSettings) {
if(settingsStrings.getContextSpecificSettingsString() != null) {
localStorage.removeItem(storageContextSpecificKey);
if(settingsStrings.getContextSpecificSettingsString() != null) {
localStorage.setItem(storageContextSpecificKey, settingsStrings.getContextSpecificSettingsString());
}
localStorage.setItem(storageContextSpecificKey, settingsStrings.getContextSpecificSettingsString());
}
}
}
private void storeSettingsStringsOnServer(SettingsStrings settingsStrings, final OnSettingsStoredCallback onSettingsStoredCallback, boolean storeGlobalSettings, boolean storeContextSpecificSettings) {
Map<String, String> keyValuePairs = new HashMap<>();
if(storeGlobalSettings) {
keyValuePairs.put(storageGlobalKey, settingsStrings.getGlobalSettingsString());
}
if(storeContextSpecificSettings) {
keyValuePairs.put(storageContextSpecificKey, settingsStrings.getContextSpecificSettingsString());
}
userService.setPreferences(keyValuePairs, new AsyncCallback<Void>() {
private void storeSettingsStringsOnServer(SettingsStrings settingsStrings, final OnSettingsStoredCallback onSettingsStoredCallback) {
AsyncCallback<Void> asyncCallback = new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
onSettingsStoredCallback.onError(caught);
}
@Override
public void onSuccess(Void result) {
onSettingsStoredCallback.onSuccess();
}
});
};
if(settingsStrings.getContextSpecificSettingsString() == null || settingsStrings.getGlobalSettingsString() == null) {
if(settingsStrings.getContextSpecificSettingsString() != null) {
userService.setPreference(storageContextSpecificKey, settingsStrings.getContextSpecificSettingsString(), asyncCallback);
}
if(settingsStrings.getGlobalSettingsString() != null) {
userService.setPreference(storageGlobalKey, settingsStrings.getGlobalSettingsString(), asyncCallback);
}
} else {
Map<String, String> keyValuePairs = new HashMap<>();
if(settingsStrings.getGlobalSettingsString() != null) {
keyValuePairs.put(storageGlobalKey, settingsStrings.getGlobalSettingsString());
}
if(settingsStrings.getContextSpecificSettingsString() != null) {
keyValuePairs.put(storageContextSpecificKey, settingsStrings.getContextSpecificSettingsString());
}
userService.setPreferences(keyValuePairs, asyncCallback);
}
}
/**
* {@inheritDoc}
*/
@Override
public void retrieveDefaultSettings(final S defaultSettings, final OnSettingsLoadedCallback<S> callback) {
if(cachedSettingsJsons == null) {
retrieveSettingsJsons(new AsyncCallback<SettingsJsons>() {
@Override
public void onFailure(Throwable caught) {
SettingsStrings localStorageSettings = retrieveSettingsStringsFromLocalStorage();
S newDefaultSettings = settingsBuildingPipeline.getSettingsObject(defaultSettings, localStorageSettings);
cachedSettingsJsons = settingsBuildingPipeline.getSettingsStringConverter().convertToSettingsJson(localStorageSettings);
callback.onError(caught, newDefaultSettings);
}
@Override
public void onSuccess(SettingsJsons settingsJsons) {
S newDefaultSettings = settingsBuildingPipeline.getSettingsObject(defaultSettings, settingsJsons);
cachedSettingsJsons = settingsJsons;
callback.onSuccess(newDefaultSettings);
}
});
} else {
S newDefaultSettings = settingsBuildingPipeline.getSettingsObject(defaultSettings, cachedSettingsJsons);
callback.onSuccess(newDefaultSettings);
}
}
/**
* {@inheritDoc}
*/
@@ -220,55 +166,13 @@ public class UserSettingsStorageManager<S extends Settings> implements SettingsS
});
}
/**
* {@inheritDoc}
*/
@Override
public JSONValue settingsToJSON(Settings newSettings, PipelineLevel pipelineLevel, List<String> path) {
return settingsBuildingPipeline.getJsonObject(newSettings, pipelineLevel, path);
}
private void storeSettingsJsons(SettingsJsons settingsJsons, OnSettingsStoredCallback onSettingsStoredCallback, boolean storeGlobalSettings, boolean storeContextSpecificSettings) {
invalidateCache();
SettingsStrings settingsStrings = settingsBuildingPipeline.getSettingsStringConverter().convertToSettingsStrings(settingsJsons);
storeSettingsStringsOnLocalStorage(settingsStrings, storeGlobalSettings, storeContextSpecificSettings);
if (userService.getCurrentUser() != null) {
storeSettingsStringsOnServer(settingsStrings, onSettingsStoredCallback, storeGlobalSettings, storeContextSpecificSettings);
} else {
onSettingsStoredCallback.onSuccess();
}
}
public void invalidateCache() {
cachedSettingsJsons = null;
}
/**
* {@inheritDoc}
*/
@Override
public void storeGlobalSettingsJson(JSONObject globalSettingsJson,
OnSettingsStoredCallback onSettingsStoredCallback) {
SettingsJsons settingsJsons = new SettingsJsons(globalSettingsJson, null);
storeSettingsJsons(settingsJsons, onSettingsStoredCallback, true, false);
}
/**
* {@inheritDoc}
*/
@Override
public void storeContextSpecificSettingsJson(JSONObject contextSpecificSettingsJson,
OnSettingsStoredCallback onSettingsStoredCallback) {
SettingsJsons settingsJsons = new SettingsJsons(null, contextSpecificSettingsJson);
storeSettingsJsons(settingsJsons, onSettingsStoredCallback, false, true);
}
/**
* {@inheritDoc}
*/
@Override
public void retrieveSettingsJsons(AsyncCallback<SettingsJsons> asyncCallback) {
retrieveSettingsCallbacksQueue.add(asyncCallback);
public void retrieveSettingsRepresentation(OnSettingsLoadedCallback<SettingsStrings> callback) {
retrieveSettingsCallbacksQueue.add(callback);
if(userStatusEventHandler == null) {
this.userStatusEventHandler = new UserStatusEventHandler() {
@@ -277,22 +181,22 @@ public class UserSettingsStorageManager<S extends Settings> implements SettingsS
//call this method always on user status change event in order to cause sync between local storage and server
if(!initialUserSetting) {
initialUserSetting = true;
requestSettingsJsons();
retrieveSettingsStringsFromServerOrLocalStorage();
}
}
};
userService.addUserStatusEventHandler(userStatusEventHandler, true);
} else if(initialUserSetting) {
requestSettingsJsons();
retrieveSettingsStringsFromServerOrLocalStorage();
}
}
private void requestSettingsJsons() {
private void retrieveSettingsStringsFromServerOrLocalStorage() {
if (userService.getCurrentUser() == null) {
SettingsStrings settingsStrings = retrieveSettingsStringsFromLocalStorage();
AsyncCallback<SettingsJsons> asyncCallback;
while((asyncCallback = retrieveSettingsCallbacksQueue.poll()) != null) {
asyncCallback.onSuccess(settingsBuildingPipeline.getSettingsStringConverter().convertToSettingsJson(settingsStrings));
OnSettingsLoadedCallback<SettingsStrings> callback;
while((callback = retrieveSettingsCallbacksQueue.poll()) != null) {
callback.onSuccess(settingsStrings);
}
} else {
retrieveSettingsStringsFromServer(new AsyncCallback<SettingsStrings>() {
@@ -300,17 +204,18 @@ public class UserSettingsStorageManager<S extends Settings> implements SettingsS
@Override
public void onSuccess(SettingsStrings serverSettingsStrings) {
serverSettingsStrings = syncLocalStorageAndServer(serverSettingsStrings);
AsyncCallback<SettingsJsons> asyncCallback;
while((asyncCallback = retrieveSettingsCallbacksQueue.poll()) != null) {
asyncCallback.onSuccess(settingsBuildingPipeline.getSettingsStringConverter().convertToSettingsJson(serverSettingsStrings));
OnSettingsLoadedCallback<SettingsStrings> callback;
while((callback = retrieveSettingsCallbacksQueue.poll()) != null) {
callback.onSuccess(serverSettingsStrings);
}
}
@Override
public void onFailure(Throwable caught) {
AsyncCallback<SettingsJsons> asyncCallback;
while((asyncCallback = retrieveSettingsCallbacksQueue.poll()) != null) {
asyncCallback.onFailure(caught);
SettingsStrings fallbackSettingsStrings = retrieveSettingsStringsFromLocalStorage();
OnSettingsLoadedCallback<SettingsStrings> callback;
while((callback = retrieveSettingsCallbacksQueue.poll()) != null) {
callback.onError(caught, fallbackSettingsStrings);
}
}
});
@@ -332,28 +237,13 @@ public class UserSettingsStorageManager<S extends Settings> implements SettingsS
public void onError(Throwable caught) {
//nothing to do
}
}, true, true);
});
}
serverSettingsStrings = localStorageSettingsStrings;
} else {
storeSettingsStringsOnLocalStorage(serverSettingsStrings, true, true);
storeSettingsStringsOnLocalStorage(serverSettingsStrings);
}
return serverSettingsStrings;
}
/**
* Creates a {@link SettingsStorageManager} instance based on the ignoreLocalSettings URL flag. if
* ignoreLocalSettings is set to <code>true</code>, a SimpleSettingsStorageManager is created. A
* UserSettingsStorageManager is created otherwise.
*/
public static <S extends Settings> SettingsStorageManager<S> createSettingsStorageManager(UserService userService, StorageDefinitionId storageDefinitionId) {
if (getIgnoreLocalSettings().isIgnoreLocalSettings()) {
return new SimpleSettingsStorageManager<>();
}
return new UserSettingsStorageManager<>(userService, storageDefinitionId);
}
public static IgnoreLocalSettings getIgnoreLocalSettings() {
return new SettingsToUrlSerializer().deserializeFromCurrentLocation(new IgnoreLocalSettings());
}
}
@@ -1,63 +0,0 @@
package com.sap.sse.security.ui.settings;
import com.sap.sse.common.settings.Settings;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.settings.PipelineLevel;
import com.sap.sse.security.ui.client.UserService;
/**
* Specialization of {@link UserSettingsStorageManager} which uses {@link UserSettingsBuildingPipelineWithPatching} for settings construction.
* This implementation provides additional methods for attachment of {@link SettingsPatch}s. There are multiple hooks in
* the underlying {@link UserSettingsBuildingPipelineWithPatching} which can be used to influence the
* settings construction in its construction pipeline. After each {@link PipelineLevel} a custom {@link SettingsPatch}
* may be applied. The patch may partially or completely modify the resulting settings object in order to provide
* the desired behavior of default settings for a dynamic environment, e.g. RaceBoard which determines its default settings regarding
* to RaceModes, PlayModes and etc.
*
* @author Vladislav Chumak
*
* @param <S>
* The {@link Settings} type of the settings of the root component/perspective containing all the settings
* for itself and its subcomponents
*/
public class UserSettingsStorageManagerWithPatching<S extends Settings> extends UserSettingsStorageManager<S> {
/**
* The typed pipeline used for the settings construction.
*/
protected final UserSettingsBuildingPipelineWithPatching settingsBuildingPipeline;
/**
* Constructs the instance with {@link UserSettingsBuildingPipelineWithPatching}.
*
* @param userService The service which is used for server-side settings storage
* @param storageDefinitionId The definition for User Settings and Document Settings storage keys
*/
public UserSettingsStorageManagerWithPatching(UserService userService, StorageDefinitionId storageDefinitionId) {
super(userService, storageDefinitionId, new UserSettingsBuildingPipelineWithPatching());
settingsBuildingPipeline = (UserSettingsBuildingPipelineWithPatching) super.settingsBuildingPipeline;
}
/**
* Adds a settings patch for transforming settings before storing them.
*
* @param component The component which the targeted settings for patching belong to
* @param pipelineLevel The pipeline level <b>AFTER</b> that the patch should be applied on settings
* @param settingsPatch The settings patch to apply on settings
*/
public<CS extends Settings> void addPatchForStoringSettings(Component<CS> component, PipelineLevel pipelineLevel, SettingsPatch<CS> settingsPatch) {
settingsBuildingPipeline.addPatchForStoringSettings(component, pipelineLevel, settingsPatch);
}
/**
* Adds a settings patch for settings object construction.
*
* @param component The component which the targeted settings for patching belong to
* @param pipelineLevel The pipeline level <b>AFTER</b> that the patch should be applied on settings
* @param settingsPatch The settings patch to apply on settings
*/
public<CS extends Settings> void addPatchForLoadingSettings(Component<CS> component, PipelineLevel pipelineLevel, SettingsPatch<CS> settingsPatch) {
settingsBuildingPipeline.addPatchForLoadingSettings(component, pipelineLevel, settingsPatch);
}
}