PersistentStateComponent saves state only in session

Answered

Hi,

I need PersistentStateComponent to save user preferences. The component works fine only while in session, but when I close and open IntelliJ the values do not persist. It seems that the issue is that the state is not saved into a file. Adding full code:

```

@State(
name = "ConfigProviderState",
storages = {
@Storage(value = "$APP_CONFIG$/configProvider.xml")
}
)
public class ConfigProvider implements ApplicationComponent, PersistentStateComponent<ConfigProvider.State> {

@NotNull
@Override
public String getComponentName() {
return getClass().getSimpleName();
}
@Override
public void disposeComponent() {}
@Override
public void initComponent() {}

public State state = new State();

public static class State {
public State() {}
public String foo;
}

@Override
@org.jetbrains.annotations.Nullable
public ConfigProvider.State getState() {
return state; //Saves all public variables to disk.
}

@Override
public void loadState(ConfigProvider.State state) {
XmlSerializerUtil.copyBean(state, this);
}


public String getFoo() {
if (this.state.foo == null) {
this.state.foo = "test";
}
return this.state.foo;
}

public void setFoo(String foo) {
this.state.foo = foo;
}
}


Plugin XML:

<extensions>
<applicationService
serviceImplementation="com.demisto.plugin.ide.ConfigProvider"
serviceInterface="com.demisto.plugin.ide.ConfigProvider"
/>
</extensions>

<application-components>
<component>
<implementation-class>com.plugin.ide.ConfigProvider</implementation-class>
<interface-class>com.plugin.ide.ConfigProvider</interface-class>
</component>
</application-components>
Calls to the component:
ConfigProvider c = ServiceManager.getService(ConfigProvider.class);
c.setFoo(value);
ConfigProvider c = ServiceManager.getService(ConfigProvider.class);
return c.getFoo();

Would highly appreciate your help here.

Thank you,
Shachar

8 comments
Comment actions Permalink

Probably, caused by using same class both as service and component.
You might want to remove "<applicationService ... />" registration from plugin.xml and use `ApplicationManager.getApplication().getComponent(ConfigProvider.class)` instead of `ServiceManager.getService(ConfigProvider.class)`.

0
Comment actions Permalink

Also, you can omit explicit `<interface-class ... />` if it duplicates implementation class.
Implementing `ApplicationComponent` interface is also not necessary (and it is deprecated).

0
Comment actions Permalink

Hi Aleksey,

Thank you for the fast response. After your suggestions, the code works. 
Now, I want to save a Map of all the user preferences (I can't predict the key values in advance), but then I experience the persistence issues again. 
I guess that maybe you can't serialize a Map to the XML? 

What would be the correct approach to save these values?

Thank you,
Shachar

0
Comment actions Permalink

Usually, it is possible to use Map/List/etc for serialisation. This requires some fragile runtime magic, though.
See https://github.com/JetBrains/intellij-community/blob/master/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchSettings.java as an example.
Please, share your implementation if you need a suggestion here.

If your case is tricky (ex: key/value types may differ), you can always serialize/deserialize your data manually (or with some help of com.intellij.util.xmlb.XmlSerializer).
See https://github.com/JetBrains/intellij-community/blob/master/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayout.java#L131 as an example (just use Element as a State for PersistentStateComponent).

0
Comment actions Permalink

Hi Aleksey, 

It should have only <String, String> pairs. I followed the code from - https://github.com/JetBrains/intellij-community/blob/4999f5293e4307870020f1d0d672a3d35a52f22d/plugins/git4idea/src/git4idea/config/GitVcsSettings.java#L53

But I still experience the same result (data is only persisted per session)

Attached is my full code:

@State(
name = "ConfigProvider",
storages = {
@Storage(StoragePathMacros.WORKSPACE_FILE)
}
)
public class ConfigProvider implements ApplicationComponent, PersistentStateComponent<ConfigProvider.State> {
public State state = new State();

@NotNull
@Override
public String getComponentName() {
return getClass().getSimpleName();
}

@Override
public void disposeComponent() {
}

@Override
public void initComponent() {
}

public static class State {
/* NOTE: member should be "public" to be saved in xml */
public Map<String, String> USER_PREFERENCES = new HashMap();
}

@Override
@org.jetbrains.annotations.Nullable
public ConfigProvider.State getState() {
return state; //Saves all public variables to disk.
}

@Override
public void loadState(@NotNull ConfigProvider.State state) {
this.state = state; //restores state from disk
}

public Map<String, String> getUserPreferences() {
return state.USER_PREFERENCES;
}

public void setUserPreference(@NotNull String path, @NotNull String value) {
state.USER_PREFERENCES.put(path, value);
}

@Nullable
public static ConfigProvider getInstance() {
return ApplicationManager.getApplication().getComponent(ConfigProvider.class);
}
}

ConfigProvider preferences = ConfigProvider.getInstance();
preferences.setUserPreference(key, value);
<application-components>
<component>
<implementation-class>com.plugin.ide.ConfigProvider</implementation-class>
</component>
</application-components>

 

Would much appreciate your help here!
Shachar

0
Comment actions Permalink

Please, check errors output.
It should have smth like "Caused by: com.intellij.configurationStore.UnknownMacroException: Unknown macro: $WORKSPACE_FILE$ in storage file spec: $WORKSPACE_FILE$" in it.
You're registering application component, but are trying to use Project-specific macro for "workspace.xml".
Replace "StoragePathMacros.WORKSPACE_FILE" with "some_config.xml" or use project component instead.

0
Comment actions Permalink

Thank Aleksey! Works beautifully. For the next time - where should I see this errors output? I didn't see any errors through notifications 

0
Comment actions Permalink

In IDE log file (Help | Show Log in <system file manager>) or in "Console"/"idea.log" tabs in Run/Debug toolwindow.

0

Please sign in to leave a comment.