Skip to content

Commit

Permalink
Fix compatibility with Apache WebConsole 4.7.0 (OpenEMS#1618)
Browse files Browse the repository at this point in the history
Latest version of Apache WebConsole does not explicitely store unchanged default configuration properties. This has certain implications on the way OpenEMS uses properties, e.g. it means that a Component that is created via Apache WebConsole with default 'id' does not have the 'id' stored as property.

See this issue for details:
https://issues.apache.org/jira/browse/FELIX-6436?focusedCommentId=17412472&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-17412472

* Read default property values from Factory
* Implement custom listConfigurations(componentId) method: this method also checks for the Component default value of the 'id' property
* Fix handling of static Component Property ID for singletons

Offtopic
* AbstractWebsocketServer: test for isShutdown
  • Loading branch information
sfeilmeier authored Sep 9, 2021
1 parent 5c9b781 commit ce020e9
Show file tree
Hide file tree
Showing 30 changed files with 359 additions and 117 deletions.
11 changes: 0 additions & 11 deletions io.openems.common/src/io/openems/common/OpenemsConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,6 @@ public class OpenemsConstants {
*/
public final static String MANUFACTURER_EMS_SERIAL_NUMBER = "";

/*
* Static OpenEMS Component-IDs
*/
public final static String CYCLE_ID = "_cycle";
public final static String COMPONENT_MANAGER_ID = "_componentManager";
public final static String PREDICTOR_MANAGER_ID = "_predictorManager";
public final static String META_ID = "_meta";
public final static String SUM_ID = "_sum";
public final static String HOST_ID = "_host";
public final static String SIMULATOR_ID = "_simulator";

public final static String POWER_DOC_TEXT = "Negative values for Consumption; positive for Production";

/*
Expand Down
13 changes: 13 additions & 0 deletions io.openems.common/src/io/openems/common/types/EdgeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,19 @@ public Optional<Property> getProperty(String key) {
return Optional.empty();
}

/**
* Gets the default value of a property.
*
* @param propertyId the Property ID
*/
public JsonElement getPropertyDefaultValue(String propertyId) {
Optional<Property> property = this.getProperty(propertyId);
if (!property.isPresent()) {
return JsonNull.INSTANCE;
}
return property.get().getDefaultValue();
}

/**
* Gets the Nature-IDs of the {@link Factory}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,9 @@ public void start() {
*/
@Override
protected void execute(Runnable command) {
this.executor.execute(command);
if (!this.executor.isShutdown()) {
this.executor.execute(command);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.time.Clock;
import java.util.List;

import io.openems.common.OpenemsConstants;
import io.openems.common.channel.Level;
import io.openems.common.exceptions.OpenemsError;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
Expand All @@ -20,6 +19,9 @@
*/
public interface ComponentManager extends OpenemsComponent, JsonApi, ClockProvider {

public final static String SINGLETON_SERVICE_PID = "Core.ComponentManager";
public final static String SINGLETON_COMPONENT_ID = "_componentManager";

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
CONFIG_NOT_ACTIVATED(Doc.of(Level.FAULT) //
.text("A configured OpenEMS Component was not activated")), //
Expand Down Expand Up @@ -201,7 +203,7 @@ public default void _setDefaultConfigurationFailed(boolean value) {
*/
@SuppressWarnings("unchecked")
public default <T extends OpenemsComponent> T getComponent(String componentId) throws OpenemsNamedException {
if (componentId.equals(OpenemsConstants.COMPONENT_MANAGER_ID)) {
if (SINGLETON_COMPONENT_ID.equals(componentId)) {
return (T) this;
}
List<OpenemsComponent> components = this.getEnabledComponents();
Expand All @@ -226,7 +228,7 @@ public default <T extends OpenemsComponent> T getComponent(String componentId) t
@SuppressWarnings("unchecked")
public default <T extends OpenemsComponent> T getPossiblyDisabledComponent(String componentId)
throws OpenemsNamedException {
if (componentId == OpenemsConstants.COMPONENT_MANAGER_ID) {
if (SINGLETON_COMPONENT_ID.equals(componentId)) {
return (T) this;
}
List<OpenemsComponent> components = this.getAllComponents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import java.io.IOException;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Hashtable;

import org.osgi.framework.Constants;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;

import com.google.common.base.Objects;

import io.openems.common.channel.AccessMode;
import io.openems.common.channel.Level;
import io.openems.common.channel.PersistencePriority;
Expand Down Expand Up @@ -346,6 +349,89 @@ public static boolean updateReferenceFilter(ConfigurationAdmin cm, String pid, S
return false;
}

/**
* Validates and possibly fixes the Component-ID of a Singleton.
*
* <p>
* Singleton Components are allowed to live only exactly once in an OpenEMS
* instance. These Components are marked with an Annotation:
*
* <pre>
* &#64;Designate(factory = false)
* </pre>
*
* By design it is required for these Singleton Components to have a predefined
* Component-ID, like '_cycle', '_sum', etc. This method makes sure the
* Component-ID matches this predefined ID - and if not automatically adjusts
* it.
*
* <p>
* Sidenote: ideally it would be possible to use the Component Annotation
*
* <pre>
* &#64;Component(property = { "id=_cycle" })
* </pre>
*
* for this purpose. Unfortunately this is not sufficient to have the 'id'
* property listed in EdgeConfig, ConfigurationAdmin, etc. This is why this
* workaround is required.
*
* <p>
* Usage:
*
* <pre>
* if (OpenemsComponent.validateSingletonComponentId(this.cm, this.serviceFactoryPid(), SINGLETON_COMPONENT_ID)) {
* return;
* }
* </pre>
*
* @param cm a ConfigurationAdmin instance. Get one using
*
* <pre>
* &#64;Reference
* ConfigurationAdmin cm;
* </pre>
*
* @param pid PID of the calling component (use 'config.service_pid()' or
* '(String)prop.get(Constants.SERVICE_PID)'; if null,
* Component-ID can not be updated.
* @param expectedId The expected predefined Component-ID
*
* @return true if the ID was updated. You may use it to abort the activate()
* method.
*/
public static boolean validateSingleton(ConfigurationAdmin cm, String pid, String expectedId) {
Configuration c;
try {
c = cm.getConfiguration(pid, "?");
Dictionary<String, Object> properties = c.getProperties();

final String actualId;
final String actualAlias;
if (properties == null) {
// trigger creation of new configuration
properties = new Hashtable<>();
actualId = null;
actualAlias = null;
} else {
actualId = (String) properties.get("id");
actualAlias = (String) properties.get("alias");
}
// Fix Component-ID if required
if (!Objects.equal(expectedId, actualId) || !Objects.equal(pid, actualAlias)) {
properties.put("id", expectedId);
properties.put("alias", pid);
c.update(properties);
return true;
}
} catch (IOException | SecurityException e) {
System.err.println(
"validateSingletonComponentId ERROR " + e.getClass().getSimpleName() + ": " + e.getMessage());
e.printStackTrace();
}
return false;
}

/**
* Update a configuration property.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

public interface Cycle extends OpenemsComponent {

public final static String SINGLETON_SERVICE_PID = "Core.Cycle";
public final static String SINGLETON_COMPONENT_ID = "_cycle";

public static final int DEFAULT_CYCLE_TIME = 1000; // in [ms]

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

public interface Host extends OpenemsComponent, JsonApi {

public final static String SINGLETON_SERVICE_PID = "Core.Host";
public final static String SINGLETON_COMPONENT_ID = "_host";

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
DISK_IS_FULL(Doc.of(Level.INFO) //
.text("Disk is full")), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

public interface Meta extends ModbusSlave {

public final static String SINGLETON_SERVICE_PID = "Core.Meta";
public final static String SINGLETON_COMPONENT_ID = "_meta";

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
/**
* OpenEMS Version
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.openems.edge.common.sum;

import io.openems.common.OpenemsConstants;
import io.openems.edge.common.channel.Channel;
import io.openems.edge.common.component.AbstractOpenemsComponent;
import io.openems.edge.common.component.OpenemsComponent;
Expand All @@ -21,7 +20,7 @@ public DummySum() {
for (Channel<?> channel : this.channels()) {
channel.nextProcessImage();
}
super.activate(null, OpenemsConstants.SUM_ID, "", true);
super.activate(null, Sum.SINGLETON_COMPONENT_ID, Sum.SINGLETON_SERVICE_PID, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
*/
public interface Sum extends OpenemsComponent {

public final static String SINGLETON_SERVICE_PID = "Core.Sum";
public final static String SINGLETON_COMPONENT_ID = "_sum";

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
/**
* Ess: Average State of Charge.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import org.osgi.service.component.ComponentContext;

import io.openems.common.OpenemsConstants;
import io.openems.common.exceptions.NotImplementedException;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.jsonrpc.base.JsonrpcRequest;
Expand Down Expand Up @@ -77,12 +76,12 @@ public EdgeConfig getEdgeConfig() {

@Override
public String id() {
return OpenemsConstants.COMPONENT_MANAGER_ID;
return ComponentManager.SINGLETON_COMPONENT_ID;
}

@Override
public String alias() {
return OpenemsConstants.COMPONENT_MANAGER_ID;
return ComponentManager.SINGLETON_COMPONENT_ID;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.openems.common.OpenemsConstants;
import io.openems.common.exceptions.NotImplementedException;
import io.openems.common.exceptions.OpenemsError;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
Expand All @@ -25,6 +24,7 @@
import io.openems.common.jsonrpc.request.UpdateUserLanguageRequest;
import io.openems.common.jsonrpc.response.AuthenticatedRpcResponse;
import io.openems.common.session.Role;
import io.openems.edge.common.component.ComponentManager;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.jsonapi.JsonApi;
import io.openems.edge.common.user.User;
Expand Down Expand Up @@ -127,7 +127,7 @@ private CompletableFuture<AuthenticatedRpcResponse> handleAuthenticatedRpcReques
private CompletableFuture<GenericJsonrpcResponseSuccess> handleGetEdgeConfigRequest(User user,
GetEdgeConfigRequest getEdgeConfigRequest) throws OpenemsNamedException {
// wrap original request inside ComponentJsonApiRequest
ComponentJsonApiRequest request = new ComponentJsonApiRequest(OpenemsConstants.COMPONENT_MANAGER_ID,
ComponentJsonApiRequest request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID,
getEdgeConfigRequest);

return this.handleComponentJsonApiRequest(user, request);
Expand All @@ -146,8 +146,8 @@ private CompletableFuture<GenericJsonrpcResponseSuccess> handleCreateComponentCo
user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.INSTALLER);

// wrap original request inside ComponentJsonApiRequest
String componentId = OpenemsConstants.COMPONENT_MANAGER_ID;
ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, createComponentConfigRequest);
ComponentJsonApiRequest request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID,
createComponentConfigRequest);

return this.handleComponentJsonApiRequest(user, request);
}
Expand All @@ -165,8 +165,8 @@ private CompletableFuture<GenericJsonrpcResponseSuccess> handleUpdateComponentCo
user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.OWNER);

// wrap original request inside ComponentJsonApiRequest
String componentId = OpenemsConstants.COMPONENT_MANAGER_ID;
ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, updateComponentConfigRequest);
ComponentJsonApiRequest request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID,
updateComponentConfigRequest);

return this.handleComponentJsonApiRequest(user, request);
}
Expand All @@ -184,8 +184,8 @@ private CompletableFuture<GenericJsonrpcResponseSuccess> handleDeleteComponentCo
user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.INSTALLER);

// wrap original request inside ComponentJsonApiRequest
String componentId = OpenemsConstants.COMPONENT_MANAGER_ID;
ComponentJsonApiRequest request = new ComponentJsonApiRequest(componentId, deleteComponentConfigRequest);
ComponentJsonApiRequest request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID,
deleteComponentConfigRequest);

return this.handleComponentJsonApiRequest(user, request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import com.google.gson.JsonObject;

import io.openems.common.OpenemsConstants;
import io.openems.common.channel.PersistencePriority;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;
Expand All @@ -32,8 +31,8 @@
public class BackendApiImplTest {

private static final String CTRL_ID = "ctrl0";
private static final String SUM_ID = Sum.SINGLETON_COMPONENT_ID;

private static final String SUM_ID = OpenemsConstants.SUM_ID;
private static final ChannelAddress SUM_GRID_ACTIVE_POWER = new ChannelAddress(SUM_ID,
Sum.ChannelId.GRID_ACTIVE_POWER.id());
private static final ChannelAddress SUM_PRODUCTION_ACTIVE_POWER = new ChannelAddress(SUM_ID,
Expand Down
Loading

0 comments on commit ce020e9

Please sign in to comment.