Added nullness annotations to Item events (#2384)

* Added nullness annotations to Item events
* Added nullness annotations to tests

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
pull/2396/head
Christoph Weitkamp 2021-05-30 18:27:15 +02:00 committed by GitHub
parent a52bb42481
commit 8acaa8994f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 83 additions and 56 deletions

View File

@ -91,7 +91,11 @@ public class ScriptBusEvent {
try {
Item item = itemRegistry.getItem(itemName);
Command command = TypeParser.parseCommand(item.getAcceptedCommandTypes(), commandString);
eventPublisher.post(ItemEventFactory.createCommandEvent(itemName, command));
if (command != null) {
eventPublisher.post(ItemEventFactory.createCommandEvent(itemName, command));
} else {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("Command '{}' cannot be parsed.", commandString);
}
} catch (ItemNotFoundException e) {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '{}' does not exist.", itemName);
}
@ -151,7 +155,11 @@ public class ScriptBusEvent {
try {
Item item = itemRegistry.getItem(itemName);
State state = TypeParser.parseState(item.getAcceptedDataTypes(), stateString);
eventPublisher.post(ItemEventFactory.createStateEvent(itemName, state));
if (state != null) {
eventPublisher.post(ItemEventFactory.createStateEvent(itemName, state));
} else {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("State '{}' cannot be parsed.", stateString);
}
} catch (ItemNotFoundException e) {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '{}' does not exist.", itemName);
}

View File

@ -12,17 +12,20 @@
*/
package org.openhab.core.events;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link EventPublisher} posts {@link Event}s through the openHAB event bus in an asynchronous way.
* Posted events can be received by implementing the {@link EventSubscriber} callback interface.
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public interface EventPublisher {
/**
* Posts an event through the event bus in an asynchronous way.
*
*
* @param event the event posted through the event bus
* @throws IllegalArgumentException if the event is null
* @throws IllegalArgumentException if one of the event properties type, payload or topic is null

View File

@ -12,19 +12,22 @@
*/
package org.openhab.core.events;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link TopicEventFilter} is a default openHAB {@link EventFilter} implementation that ensures filtering
* of events based on an event topic.
*
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public class TopicEventFilter implements EventFilter {
private final String topicRegex;
/**
* Constructs a new topic event filter.
*
*
* @param topicRegex the regular expression of a topic
* @see <a href="http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html">Java Regex</a>
*/

View File

@ -20,6 +20,7 @@ import java.util.Hashtable;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventPublisher;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.EventAdmin;
@ -36,17 +37,13 @@ import org.osgi.service.event.EventAdmin;
@Component
public class OSGiEventPublisher implements EventPublisher {
private EventAdmin osgiEventAdmin;
private final EventAdmin osgiEventAdmin;
@Reference
protected void setEventAdmin(EventAdmin eventAdmin) {
@Activate
public OSGiEventPublisher(final @Reference EventAdmin eventAdmin) {
this.osgiEventAdmin = eventAdmin;
}
protected void unsetEventAdmin(EventAdmin eventAdmin) {
this.osgiEventAdmin = null;
}
@Override
public void post(final Event event) throws IllegalArgumentException, IllegalStateException {
EventAdmin eventAdmin = this.osgiEventAdmin;
@ -64,8 +61,9 @@ public class OSGiEventPublisher implements EventPublisher {
properties.put("type", event.getType());
properties.put("payload", event.getPayload());
properties.put("topic", event.getTopic());
if (event.getSource() != null) {
properties.put("source", event.getSource());
String source = event.getSource();
if (source != null) {
properties.put("source", source);
}
eventAdmin.postEvent(new org.osgi.service.event.Event("openhab", properties));
return null;

View File

@ -14,6 +14,8 @@ package org.openhab.core.items.events;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
@ -28,6 +30,7 @@ import org.openhab.core.events.EventSubscriber;
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractItemEventSubscriber implements EventSubscriber {
private final Set<String> subscribedEventTypes = Set.of(ItemStateEvent.TYPE, ItemCommandEvent.TYPE);
@ -38,7 +41,7 @@ public abstract class AbstractItemEventSubscriber implements EventSubscriber {
}
@Override
public EventFilter getEventFilter() {
public @Nullable EventFilter getEventFilter() {
return null;
}

View File

@ -12,6 +12,8 @@
*/
package org.openhab.core.items.events;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.AbstractEvent;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.dto.ItemDTO;
@ -22,6 +24,7 @@ import org.openhab.core.items.dto.ItemDTO;
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractItemRegistryEvent extends AbstractEvent {
private final ItemDTO item;
@ -31,17 +34,17 @@ public abstract class AbstractItemRegistryEvent extends AbstractEvent {
*
* @param topic the topic
* @param payload the payload
* @param source the source, can be null
* @param source the source
* @param item the item data transfer object
*/
protected AbstractItemRegistryEvent(String topic, String payload, String source, ItemDTO item) {
protected AbstractItemRegistryEvent(String topic, String payload, @Nullable String source, ItemDTO item) {
super(topic, payload, source);
this.item = item;
}
/**
* Gets the item.
*
*
* @return the item
*/
public ItemDTO getItem() {

View File

@ -12,6 +12,7 @@
*/
package org.openhab.core.items.events;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.items.dto.ItemDTO;
/**
@ -20,6 +21,7 @@ import org.openhab.core.items.dto.ItemDTO;
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public class ItemAddedEvent extends AbstractItemRegistryEvent {
/**

View File

@ -16,9 +16,10 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.AbstractEventFactory;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFactory;
@ -39,8 +40,8 @@ import org.osgi.service.component.annotations.Component;
* @author Stefan Bußweiler - Initial contribution
*/
@Component(immediate = true, service = EventFactory.class)
@NonNullByDefault
public class ItemEventFactory extends AbstractEventFactory {
private static final String TYPE_POSTFIX = "Type";
private static final String CORE_LIBRARY_PACKAGE = "org.openhab.core.library.types.";
@ -65,13 +66,14 @@ public class ItemEventFactory extends AbstractEventFactory {
* Constructs a new ItemEventFactory.
*/
public ItemEventFactory() {
super(Stream.of(ItemCommandEvent.TYPE, ItemStateEvent.TYPE, ItemStatePredictedEvent.TYPE,
super(Set.of(ItemCommandEvent.TYPE, ItemStateEvent.TYPE, ItemStatePredictedEvent.TYPE,
ItemStateChangedEvent.TYPE, ItemAddedEvent.TYPE, ItemUpdatedEvent.TYPE, ItemRemovedEvent.TYPE,
GroupItemStateChangedEvent.TYPE).collect(Collectors.toSet()));
GroupItemStateChangedEvent.TYPE));
}
@Override
protected Event createEventByType(String eventType, String topic, String payload, String source) throws Exception {
protected Event createEventByType(String eventType, String topic, String payload, @Nullable String source)
throws Exception {
if (ItemCommandEvent.TYPE.equals(eventType)) {
return createCommandEvent(topic, payload, source);
} else if (ItemStateEvent.TYPE.equals(eventType)) {
@ -101,14 +103,14 @@ public class ItemEventFactory extends AbstractEventFactory {
return new GroupItemStateChangedEvent(topic, payload, itemName, memberName, state, oldState);
}
private Event createCommandEvent(String topic, String payload, String source) {
private Event createCommandEvent(String topic, String payload, @Nullable String source) {
String itemName = getItemName(topic);
ItemEventPayloadBean bean = deserializePayload(payload, ItemEventPayloadBean.class);
Command command = parseType(bean.getType(), bean.getValue(), Command.class);
return new ItemCommandEvent(topic, payload, itemName, command, source);
}
private Event createStateEvent(String topic, String payload, String source) {
private Event createStateEvent(String topic, String payload, @Nullable String source) {
String itemName = getItemName(topic);
ItemEventPayloadBean bean = deserializePayload(payload, ItemEventPayloadBean.class);
State state = getState(bean.getType(), bean.getValue());
@ -165,7 +167,7 @@ public class ItemEventFactory extends AbstractEventFactory {
return desiredClass.cast(parsedObject);
}
private Object parseSimpleClassName(String simpleClassName, String valueToParse) {
private @Nullable Object parseSimpleClassName(String simpleClassName, String valueToParse) {
if (simpleClassName.equals(UnDefType.class.getSimpleName())) {
return UnDefType.valueOf(valueToParse);
}
@ -176,7 +178,7 @@ public class ItemEventFactory extends AbstractEventFactory {
try {
Class<?> stateClass = Class.forName(CORE_LIBRARY_PACKAGE + simpleClassName);
Method valueOfMethod = stateClass.getMethod("valueOf", String.class);
return valueOfMethod.invoke(null, valueToParse);
return valueOfMethod.invoke(stateClass, valueToParse);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Error getting class for simple name: '" + simpleClassName
+ "' using package name '" + CORE_LIBRARY_PACKAGE + "'.", e);
@ -217,7 +219,7 @@ public class ItemEventFactory extends AbstractEventFactory {
* @return the created item command event
* @throws IllegalArgumentException if itemName or command is null
*/
public static ItemCommandEvent createCommandEvent(String itemName, Command command, String source) {
public static ItemCommandEvent createCommandEvent(String itemName, Command command, @Nullable String source) {
assertValidArguments(itemName, command, "command");
String topic = buildTopic(ITEM_COMAND_EVENT_TOPIC, itemName);
ItemEventPayloadBean bean = new ItemEventPayloadBean(getCommandType(command), command.toString());
@ -246,7 +248,7 @@ public class ItemEventFactory extends AbstractEventFactory {
* @return the created item state event
* @throws IllegalArgumentException if itemName or state is null
*/
public static ItemStateEvent createStateEvent(String itemName, State state, String source) {
public static ItemStateEvent createStateEvent(String itemName, State state, @Nullable String source) {
assertValidArguments(itemName, state, "state");
String topic = buildTopic(ITEM_STATE_EVENT_TOPIC, itemName);
ItemEventPayloadBean bean = new ItemEventPayloadBean(getStateType(state), state.toFullString());
@ -415,8 +417,8 @@ public class ItemEventFactory extends AbstractEventFactory {
* This is a java bean that is used to serialize/deserialize item event payload.
*/
private static class ItemEventPayloadBean {
private String type;
private String value;
private @NonNullByDefault({}) String type;
private @NonNullByDefault({}) String value;
/**
* Default constructor for deserialization e.g. by Gson.
@ -443,8 +445,8 @@ public class ItemEventFactory extends AbstractEventFactory {
* This is a java bean that is used to serialize/deserialize item state changed event payload.
*/
private static class ItemStatePredictedEventPayloadBean {
private String predictedType;
private String predictedValue;
private @NonNullByDefault({}) String predictedType;
private @NonNullByDefault({}) String predictedValue;
private boolean isConfirmation;
/**
@ -477,10 +479,10 @@ public class ItemEventFactory extends AbstractEventFactory {
* This is a java bean that is used to serialize/deserialize item state changed event payload.
*/
private static class ItemStateChangedEventPayloadBean {
private String type;
private String value;
private String oldType;
private String oldValue;
private @NonNullByDefault({}) String type;
private @NonNullByDefault({}) String value;
private @NonNullByDefault({}) String oldType;
private @NonNullByDefault({}) String oldValue;
/**
* Default constructor for deserialization e.g. by Gson.

View File

@ -12,6 +12,7 @@
*/
package org.openhab.core.items.events;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.items.dto.ItemDTO;
/**
@ -20,6 +21,7 @@ import org.openhab.core.items.dto.ItemDTO;
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public class ItemRemovedEvent extends AbstractItemRegistryEvent {
/**

View File

@ -12,6 +12,7 @@
*/
package org.openhab.core.items.events;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.items.dto.ItemDTO;
/**
@ -20,6 +21,7 @@ import org.openhab.core.items.dto.ItemDTO;
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public class ItemUpdatedEvent extends AbstractItemRegistryEvent {
private final ItemDTO oldItem;
@ -49,7 +51,7 @@ public class ItemUpdatedEvent extends AbstractItemRegistryEvent {
/**
* Gets the old item.
*
*
* @return the oldItem
*/
public ItemDTO getOldItem() {

View File

@ -10,18 +10,20 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.items.events;
package org.openhab.core.events;
import static org.junit.jupiter.api.Assertions.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.core.events.AbstractEventFactory;
import org.openhab.core.items.events.ItemEventFactory;
/**
* {@link AbstractEventFactoryTests} tests the {@link AbstractEventFactory}.
* {@link AbstractEventFactoryTests} tests the {@link org.openhab.core.events.AbstractEventFactory}.
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public class AbstractEventFactoryTest {
private final ItemEventFactory factory = new ItemEventFactory();

View File

@ -16,6 +16,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.core.events.Event;
import org.openhab.core.items.Item;
@ -36,6 +37,7 @@ import com.google.gson.Gson;
*
* @author Stefan Bußweiler - Initial contribution
*/
@NonNullByDefault
public class ItemEventFactoryTest {
private final ItemEventFactory factory = new ItemEventFactory();

View File

@ -59,6 +59,7 @@ import org.openhab.core.library.types.OnOffType;
import org.openhab.core.service.ReadyMarker;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.test.storage.VolatileStorageService;
import org.openhab.core.types.Command;
import org.openhab.core.types.TypeParser;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
@ -417,8 +418,11 @@ public class RuntimeRuleTest extends JavaOSGiTest {
final ItemRegistry itemRegistry = getService(ItemRegistry.class);
final EventPublisher eventPublisher = getService(EventPublisher.class);
final Item myMotionItem = itemRegistry.getItem("myMotionItem3");
eventPublisher.post(ItemEventFactory.createCommandEvent("myMotionItem3",
TypeParser.parseCommand(myMotionItem.getAcceptedCommandTypes(), "ON")));
Command command = TypeParser.parseCommand(myMotionItem.getAcceptedCommandTypes(), "ON");
assertNotNull(command);
if (command != null) {
eventPublisher.post(ItemEventFactory.createCommandEvent("myMotionItem3", command));
}
waitForAssert(() -> {
assertEquals(RuleStatusDetail.DISABLED, ruleEngine.getStatusInfo(firstRuleUID).getStatusDetail());
@ -438,8 +442,11 @@ public class RuntimeRuleTest extends JavaOSGiTest {
.build();
ruleRegistry.add(rule2);
eventPublisher.post(ItemEventFactory.createCommandEvent("myMotionItem3",
TypeParser.parseCommand(myMotionItem.getAcceptedCommandTypes(), "OFF")));
command = TypeParser.parseCommand(myMotionItem.getAcceptedCommandTypes(), "OFF");
assertNotNull(command);
if (command != null) {
eventPublisher.post(ItemEventFactory.createCommandEvent("myMotionItem3", command));
}
waitForAssert(() -> {
assertEquals(RuleStatus.IDLE, ruleEngine.getStatus(firstRuleUID));

View File

@ -180,16 +180,6 @@ public class OSGiEventManagerOSGiTest extends JavaOSGiTest {
assertEventCount(subscriber4, 0);
}
@Test
public void testValidationEvent() {
try {
eventPublisher.post(null);
fail("IllegalArgumentException expected!");
} catch (IllegalArgumentException e) {
assertEquals("Argument 'event' must not be null.", e.getMessage());
}
}
@Test
public void testValidationType() {
Event event = createEvent(null, "{a: 'A', b: 'B'}", TOPIC);