Skip to content

Commit

Permalink
Merge pull request #536 from eclipse-sensinact/feature/timed-value
Browse files Browse the repository at this point in the history
Add a default TimedValue implementation
  • Loading branch information
timothyjward authored Jan 23, 2025
2 parents 5945d25 + 554b85b commit 88c2243
Show file tree
Hide file tree
Showing 37 changed files with 99 additions and 198 deletions.
2 changes: 1 addition & 1 deletion core/api/dependency-check.bndrun
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-runrequires: bnd.identity;id='${project.groupId}.${project.artifactId}'
-runfw: org.apache.felix.framework
-runee: JavaSE-11
-runee: JavaSE-17

# These packages are also provided by the gecko.emf.osgi.component project
# so we blacklist the API for resolve consistency
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.sensinact.core.model.ResourceType;
import org.eclipse.sensinact.core.model.ValueType;
import org.eclipse.sensinact.core.notification.ResourceDataNotification;
import org.eclipse.sensinact.core.twin.DefaultTimedValue;
import org.eclipse.sensinact.core.twin.TimedValue;
import org.eclipse.sensinact.gateway.geojson.GeoJsonObject;

Expand Down Expand Up @@ -150,17 +151,7 @@ public Class<?> getType() {

@Override
public TimedValue<?> getValue() {
return new TimedValue<Object>() {
@Override
public Instant getTimestamp() {
return service.provider.rdn.timestamp;
}

@Override
public Object getValue() {
return service.provider.rdn.newValue;
}
};
return new DefaultTimedValue<>(service.provider.rdn.newValue, service.provider.rdn.timestamp);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,28 @@
* Contributors:
* Kentyou - initial implementation
**********************************************************************/
package org.eclipse.sensinact.core.twin.impl;
package org.eclipse.sensinact.core.twin;

import java.time.Instant;

import org.eclipse.sensinact.core.twin.TimedValue;
public record DefaultTimedValue<T> (T value, Instant timestamp) implements TimedValue<T> {

public class TimedValueImpl<T> implements TimedValue<T> {
public static final DefaultTimedValue<?> EMPTY = new DefaultTimedValue<>();

private final Instant timestamp;

private final T value;

public TimedValueImpl(final T value) {
this(value, Instant.now());
/**
* A shortcut for creating an empty TimedValue with no value or timestamp
* @param value
*/
public DefaultTimedValue() {
this(null, null);
}

public TimedValueImpl(final T value, Instant instant) {
this.value = value;
this.timestamp = instant;
/**
* A shortcut for creating a value with the current time
* @param value
*/
public DefaultTimedValue(T value) {
this(value, Instant.now());
}

@Override
Expand All @@ -40,9 +43,4 @@ public Instant getTimestamp() {
public T getValue() {
return value;
}

@Override
public String toString() {
return String.format("TimedValue(%s, %s)", getValue(), getTimestamp());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,20 @@

import java.time.Instant;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize(as = DefaultTimedValue.class)
public interface TimedValue<T> {

Instant getTimestamp();

T getValue();

/**
* @return true if this {@link TimedValue} has no timestamp
* and is therefore an empty marker value
*/
default boolean isEmpty() {
return getTimestamp() == null;
}
}
2 changes: 1 addition & 1 deletion core/emf-api/dependency-check.bndrun
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-runrequires: bnd.identity;id='${project.groupId}.${project.artifactId}'
-runfw: org.apache.felix.framework
-runee: JavaSE-11
-runee: JavaSE-17

# This blacklist ensures consistent resolution locally and in CI
-runblacklist: bnd.identity;id='org.osgi.service.cm'
Expand Down
2 changes: 1 addition & 1 deletion core/impl/integration-test.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
bnd.identity;id='ch.qos.logback.classic'
-resolve.effective: active

-runee: JavaSE-11
-runee: JavaSE-17
-runfw: org.apache.felix.framework
-runproperties: \
org.osgi.framework.bootdelegation=org.mockito.internal.creation.bytebuddy.inject,\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
import org.eclipse.sensinact.core.model.nexus.emf.NamingUtils;
import org.eclipse.sensinact.core.model.nexus.emf.compare.EMFCompareUtil;
import org.eclipse.sensinact.core.notification.impl.NotificationAccumulator;
import org.eclipse.sensinact.core.twin.DefaultTimedValue;
import org.eclipse.sensinact.core.twin.TimedValue;
import org.eclipse.sensinact.core.twin.impl.TimedValueImpl;
import org.eclipse.sensinact.core.whiteboard.impl.SensinactWhiteboard;
import org.eclipse.sensinact.model.core.metadata.Action;
import org.eclipse.sensinact.model.core.metadata.ActionParameter;
Expand Down Expand Up @@ -715,13 +715,13 @@ public TimedValue<Object> getResourceMetadataValue(Provider provider, String ser
if (metadata != null) {
for (FeatureCustomMetadata entry : metadata.getExtra()) {
if (entry.getName().equals(key)) {
return new TimedValueImpl<Object>(entry.getValue(), entry.getTimestamp());
return new DefaultTimedValue<Object>(entry.getValue(), entry.getTimestamp());
}
}
// If the resource exists but has no metadata for that key then return an
// empty timed value indicating that the resource exists but the metadata
// is not set
return new TimedValueImpl<Object>(null, null);
return new DefaultTimedValue<Object>(null, null);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.eclipse.sensinact.core.snapshot.ProviderSnapshot;
import org.eclipse.sensinact.core.snapshot.ResourceSnapshot;
import org.eclipse.sensinact.core.snapshot.ServiceSnapshot;
import org.eclipse.sensinact.core.twin.DefaultTimedValue;
import org.eclipse.sensinact.core.twin.SensinactResource;
import org.eclipse.sensinact.core.twin.SensinactService;
import org.eclipse.sensinact.core.twin.TimedValue;
Expand Down Expand Up @@ -267,12 +268,12 @@ private <T> TimedValue<T> getResourceValue(Provider provider, String serviceName
// Check value type
final Object rawValue = svc.eGet(rcFeature);
if (rawValue == null) {
return new TimedValueImpl<T>(null, timestamp);
return new DefaultTimedValue<T>(null, timestamp);
} else if (!type.isAssignableFrom(rawValue.getClass())) {
throw new IllegalArgumentException(
"Expected a " + type.getName() + " but resource is a " + rawValue.getClass().getName());
} else {
return new TimedValueImpl<T>(type.cast(rawValue), timestamp);
return new DefaultTimedValue<T>(type.cast(rawValue), timestamp);
}
}

Expand Down Expand Up @@ -319,7 +320,7 @@ private void fillInResource(final ResourceSnapshotImpl rcSnapshot) {
}

rcSnapshot.setValue(
new TimedValueImpl<Object>(svc == null ? null : svc.eGet((EStructuralFeature) rcFeature), timestamp));
new DefaultTimedValue<Object>(svc == null ? null : svc.eGet((EStructuralFeature) rcFeature), timestamp));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.sensinact.core.model.ValueType;
import org.eclipse.sensinact.core.model.impl.ResourceImpl;
import org.eclipse.sensinact.core.model.nexus.ModelNexus;
import org.eclipse.sensinact.core.twin.DefaultTimedValue;
import org.eclipse.sensinact.core.twin.SensinactResource;
import org.eclipse.sensinact.core.twin.SensinactService;
import org.eclipse.sensinact.core.twin.TimedValue;
Expand Down Expand Up @@ -126,7 +127,7 @@ private <T> TimedValue<T> getValueFromTwin(final Class<T> type) {
currentTimestamp = null;
}

return new TimedValueImpl<T>(currentValue, currentTimestamp);
return new DefaultTimedValue<T>(currentValue, currentTimestamp);
}

@SuppressWarnings("unchecked")
Expand All @@ -152,7 +153,7 @@ public <T> Promise<Void> setValue(T value, Instant timestamp) {
if (hasExternalSetter) {
// Check new value type
final TimedValue<?> cachedValue = getValueFromTwin(type);
final TimedValue<T> newValue = new TimedValueImpl<T>(value, timestamp);
final TimedValue<T> newValue = new DefaultTimedValue<T>(value, timestamp);
return modelNexus.pushValue(provider, serviceName, resource, (Class<T>) type,
(TimedValue<T>) cachedValue, newValue).map(x -> null);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import org.eclipse.sensinact.core.annotation.dto.NullAction;
import org.eclipse.sensinact.core.annotation.verb.GetParam;
import org.eclipse.sensinact.core.annotation.verb.GetParam.GetSegment;
import org.eclipse.sensinact.core.twin.DefaultTimedValue;
import org.eclipse.sensinact.core.twin.TimedValue;
import org.eclipse.sensinact.core.twin.impl.TimedValueImpl;
import org.eclipse.sensinact.core.whiteboard.WhiteboardGet;
import org.osgi.util.promise.Promise;
import org.osgi.util.promise.PromiseFactory;
Expand Down Expand Up @@ -64,14 +64,14 @@ public Promise<TimedValue<Object>> pullValue(PromiseFactory pf, String modelPack
return pf.resolved(null);
case UPDATE_IF_PRESENT:
return pf.resolved(cachedValue == null || cachedValue.getTimestamp() == null ? null
: new TimedValueImpl<Object>(null));
: new DefaultTimedValue<Object>(null));
case UPDATE:
return pf.resolved(new TimedValueImpl<Object>(null));
return pf.resolved(new DefaultTimedValue<Object>(null));
default:
return pf.failed(new IllegalArgumentException("Unknown null action: " + nullAction));
}
} else if (resourceType.isAssignableFrom(result.getClass())) {
return pf.resolved(new TimedValueImpl<Object>(resourceType.cast(result)));
return pf.resolved(new DefaultTimedValue<Object>(resourceType.cast(result)));
} else {
return pf.failed(new Exception("Invalid result type: " + result.getClass()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

import org.eclipse.sensinact.core.annotation.verb.SetParam;
import org.eclipse.sensinact.core.annotation.verb.SetParam.SetSegment;
import org.eclipse.sensinact.core.twin.DefaultTimedValue;
import org.eclipse.sensinact.core.twin.TimedValue;
import org.eclipse.sensinact.core.twin.impl.TimedValueImpl;
import org.eclipse.sensinact.core.whiteboard.WhiteboardSet;
import org.osgi.util.promise.Promise;
import org.osgi.util.promise.PromiseFactory;
Expand Down Expand Up @@ -51,7 +51,7 @@ public Promise<TimedValue<Object>> pushValue(PromiseFactory pf, String modelPack
} else if (o == null) {
return pf.resolved(null);
} else if (resourceType.isAssignableFrom(o.getClass())) {
return pf.resolved(new TimedValueImpl<Object>(resourceType.cast(o)));
return pf.resolved(new DefaultTimedValue<Object>(resourceType.cast(o)));
} else {
return pf.failed(new Exception("Invalid result type: " + o.getClass()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@
import org.eclipse.sensinact.core.model.ResourceBuilder;
import org.eclipse.sensinact.core.model.SensinactModelManager;
import org.eclipse.sensinact.core.model.Service;
import org.eclipse.sensinact.core.twin.DefaultTimedValue;
import org.eclipse.sensinact.core.twin.SensinactDigitalTwin;
import org.eclipse.sensinact.core.twin.SensinactProvider;
import org.eclipse.sensinact.core.twin.SensinactResource;
import org.eclipse.sensinact.core.twin.SensinactService;
import org.eclipse.sensinact.core.twin.TimedValue;
import org.eclipse.sensinact.core.twin.impl.TimedValueImpl;
import org.eclipse.sensinact.core.whiteboard.AbstractDescriptiveAct;
import org.eclipse.sensinact.core.whiteboard.AbstractDescriptiveReadOnly;
import org.eclipse.sensinact.core.whiteboard.AbstractDescriptiveReadWrite;
Expand Down Expand Up @@ -569,7 +569,7 @@ public TimedValue<String> doMultiResource(@UriParam(UriSegment.RESOURCE) String
break;
}

return new TimedValueImpl<String>(value, newValue.getTimestamp());
return new DefaultTimedValue<String>(value, newValue.getTimestamp());
}
}

Expand Down Expand Up @@ -644,7 +644,7 @@ public TimedValue<Content> doGet(@UriParam(UriSegment.RESOURCE) String resource,
content.oldValue = null;
content.newValue = resource;
}
return new TimedValueImpl<Content>(content,
return new DefaultTimedValue<Content>(content,
cached.getTimestamp() == null ? Instant.now() : cached.getTimestamp());
}

Expand All @@ -656,7 +656,7 @@ public TimedValue<Content> doSet(@UriParam(UriSegment.RESOURCE) String resource,
Content content = new Content();
content.oldValue = cached.getValue() != null ? cached.getValue().newValue : null;
content.newValue = newValue.getValue();
return new TimedValueImpl<Content>(content, newValue.getTimestamp());
return new DefaultTimedValue<Content>(content, newValue.getTimestamp());
}
}

Expand Down Expand Up @@ -771,7 +771,7 @@ void testReadOnly() throws Throwable {
@Override
public Promise<TimedValue<Integer>> doPullValue(PromiseFactory pf, String modelPackageUri, String model,
String provider, String service, String resource, TimedValue<Integer> cachedValue) {
return pf.resolved(new TimedValueImpl<>(42));
return pf.resolved(new DefaultTimedValue<>(42));
}
};

Expand Down Expand Up @@ -801,15 +801,15 @@ void testReadWrite() throws Throwable {
@Override
public Promise<TimedValue<Long>> doPullValue(PromiseFactory pf, String modelPackageUri, String model,
String provider, String service, String resource, TimedValue<Long> cachedValue) {
return pf.resolved(new TimedValueImpl<Long>(value));
return pf.resolved(new DefaultTimedValue<Long>(value));
}

@Override
public Promise<TimedValue<Long>> doPushValue(PromiseFactory pf, String modelPackageUri, String model,
String provider, String service, String resource, TimedValue<Long> cachedValue,
TimedValue<Long> newValue) {
this.value = newValue.getValue();
return pf.resolved(new TimedValueImpl<Long>(value));
return pf.resolved(new DefaultTimedValue<Long>(value));
}
};

Expand Down Expand Up @@ -977,7 +977,7 @@ void makeActionResource(final String modelName, final String serviceName, final
@Test
void testReadOnly() throws Throwable {
WhiteboardGet<Integer> getHandler = (pf, modelPackageUri, model, provider, service, resource, resourceType,
cachedValue) -> pf.resolved(new TimedValueImpl<>(42));
cachedValue) -> pf.resolved(new DefaultTimedValue<>(42));

final String modelName = "wbHandlerROTest";
final String svcName = "svc";
Expand Down Expand Up @@ -1011,15 +1011,15 @@ void testReadWrite() throws Throwable {
public Promise<TimedValue<Long>> pullValue(PromiseFactory pf, String modelPackageUri, String model,
String provider, String service, String resource, Class<Long> resourceType,
TimedValue<Long> cachedValue) {
return pf.resolved(new TimedValueImpl<Long>(value));
return pf.resolved(new DefaultTimedValue<Long>(value));
}

@Override
public Promise<TimedValue<Long>> pushValue(PromiseFactory pf, String modelPackageUri, String model,
String provider, String service, String resource, Class<Long> resourceType,
TimedValue<Long> cachedValue, TimedValue<Long> newValue) {
this.value = newValue.getValue();
return pf.resolved(new TimedValueImpl<Long>(value));
return pf.resolved(new DefaultTimedValue<Long>(value));
}
};

Expand Down Expand Up @@ -1177,9 +1177,9 @@ void makeActionResource(final String modelName, final String serviceName, final
@Test
void testHandlersProviderFilter() throws Throwable {
WhiteboardGet<Integer> h1 = (pf, modelPackageUri, model, provider, service, resource, resourceType,
cachedValue) -> pf.resolved(new TimedValueImpl<>(1));
cachedValue) -> pf.resolved(new DefaultTimedValue<>(1));
WhiteboardGet<Integer> h2 = (pf, modelPackageUri, model, provider, service, resource, resourceType,
cachedValue) -> pf.resolved(new TimedValueImpl<>(2));
cachedValue) -> pf.resolved(new DefaultTimedValue<>(2));

final String modelName = "wbHandlerPriority";
final String svcName = "svc";
Expand Down Expand Up @@ -1242,11 +1242,11 @@ void testHandlersProviderFilter() throws Throwable {
@Test
void testHandlersWildcardFilter() throws Throwable {
WhiteboardGet<Integer> h1 = (pf, modelPackageUri, model, provider, service, resource, resourceType,
cachedValue) -> pf.resolved(new TimedValueImpl<>(1));
cachedValue) -> pf.resolved(new DefaultTimedValue<>(1));
WhiteboardGet<Integer> h2 = (pf, modelPackageUri, model, provider, service, resource, resourceType,
cachedValue) -> pf.resolved(new TimedValueImpl<>(2));
cachedValue) -> pf.resolved(new DefaultTimedValue<>(2));
WhiteboardGet<Integer> h3 = (pf, modelPackageUri, model, provider, service, resource, resourceType,
cachedValue) -> pf.resolved(new TimedValueImpl<>(3));
cachedValue) -> pf.resolved(new DefaultTimedValue<>(3));

final String modelName = "wbHandlerPriority";
final String svcName1 = "svc1";
Expand Down
2 changes: 1 addition & 1 deletion distribution/launcher/export.bndrun
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-runee: JavaSE-11
-runee: JavaSE-17
-runrequires: \
bnd.identity;id='org.eclipse.sensinact.gateway.distribution.launcher',\
bnd.identity;id='org.apache.aries.spifly.dynamic.framework.extension',\
Expand Down
2 changes: 1 addition & 1 deletion filters/ldap/integration-test.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
bnd.identity;id='ch.qos.logback.classic'
-resolve.effective: active

-runee: JavaSE-11
-runee: JavaSE-17
-runfw: org.apache.felix.framework
-runproperties: logback.configurationFile=${project.build.testOutputDirectory}/logback-test.xml

Expand Down
Loading

0 comments on commit 88c2243

Please sign in to comment.