Skip to content

Commit

Permalink
[hueemulation] Fix warnings/stacktraces at startup (#5709)
Browse files Browse the repository at this point in the history
* Properly annotates optional references with nullable to fix NPEs
* Reworked HueEmulationConfigWithRuntime.startNow to prevent IllegalStateException

Fixes #5622

Signed-off-by: Wouter Born <github@maindrain.net>
  • Loading branch information
wborn authored and J-N-K committed Jun 10, 2019
1 parent ecd8095 commit 51873b5
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont
// Don't fail the service if the upnp server does not come up
// That part is required for discovery only but does not affect already configured hue applications
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
protected @NonNullByDefault({}) UpnpServer discovery;
protected @Nullable UpnpServer discovery;
@Reference
protected @NonNullByDefault({}) ConfigStore cs;
@Reference
Expand Down Expand Up @@ -201,10 +201,11 @@ public void handleEvent(@Nullable Event event) {
initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "false");
initParams.put(ServletProperties.PROVIDER_WEB_APP, "false");
httpService.registerServlet(RESTAPI_PATH, new ServletContainer(resourceConfig), initParams, null);
if (discovery == null) {
UpnpServer localDiscovery = discovery;
if (localDiscovery == null) {
logger.warn("The UPnP Server service has not been started!");
} else if (!discovery.upnpAnnouncementThreadRunning()) {
discovery.handleEvent(null);
} else if (!localDiscovery.upnpAnnouncementThreadRunning()) {
localDiscovery.handleEvent(null);
}
statusResource.startUpnpSelfTest();
logger.info("Hue Emulation service available under {}", RESTAPI_PATH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import javax.ws.rs.core.UriInfo;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.common.registry.RegistryChangeListener;
import org.eclipse.smarthome.core.events.EventPublisher;
import org.eclipse.smarthome.core.items.GenericItem;
Expand Down Expand Up @@ -111,7 +112,7 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@Reference
protected @NonNullByDefault({}) ItemRegistry itemRegistry;
@Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL)
protected volatile @NonNullByDefault({}) EventPublisher eventPublisher;
protected volatile @Nullable EventPublisher eventPublisher;

/**
* Registers to the {@link ItemRegistry} and enumerates currently existing items.
Expand Down Expand Up @@ -373,9 +374,10 @@ public Response setLightStateApi(@Context UriInfo uri, //

// If a command could be created, post it to the framework now
if (command != null) {
if (eventPublisher != null) {
EventPublisher localEventPublisher = eventPublisher;
if (localEventPublisher != null) {
logger.debug("sending {} to {}", command, itemUID);
eventPublisher.post(ItemEventFactory.createCommandEvent(itemUID, command, "hueemulation"));
localEventPublisher.post(ItemEventFactory.createCommandEvent(itemUID, command, "hueemulation"));
} else {
logger.warn("No event publisher. Cannot post item '{}' command!", itemUID);
}
Expand Down Expand Up @@ -418,8 +420,10 @@ public Response setGroupActionApi(@Context UriInfo uri, //
// If a command could be created, post it to the framework now
if (command != null) {
logger.debug("sending {} to {}", command, id);
if (eventPublisher != null) {
eventPublisher.post(ItemEventFactory.createCommandEvent(groupItem.getUID(), command, "hueemulation"));
EventPublisher localEventPublisher = eventPublisher;
if (localEventPublisher != null) {
localEventPublisher
.post(ItemEventFactory.createCommandEvent(groupItem.getUID(), command, "hueemulation"));
} else {
logger.warn("No event publisher. Cannot post item '{}' command!", groupItem.getUID());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
@NonNullByDefault
public class StatusResource implements RegistryListener {
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
protected @NonNullByDefault({}) UpnpServer discovery;
protected @Nullable UpnpServer discovery;
@Reference
protected @NonNullByDefault({}) ConfigStore cs;
@Reference
Expand Down Expand Up @@ -125,12 +125,11 @@ private static String TR(String s) {
@Path("status")
@Produces("text/html")
public String getStatus() {
if (discovery == null) { // Optional service wiring
UpnpServer localDiscovery = discovery;
if (localDiscovery == null) { // Optional service wiring
return "UPnP Server service not started!";
}

int httpPort = discovery.getDefaultport();

String format = "<html><body><h1>Self test</h1>" + //
"<p>To access any links you need be in pairing mode!</p>" + //
"<p>Pairing mode: %s (%s) <a href='%s/api/status/link'>Enable</a> | <a href='%s/api/status/link?V1=true'>Enable with bridge V1 emulation</a></p>"
Expand All @@ -145,18 +144,18 @@ public String getStatus() {
+ //
"<h2>Users</h2><ul>%s</ul></body></html>";

final String users = cs.ds.config.whitelist.entrySet().stream().map(user -> "<li>" + user.getKey() + " <b>"
String users = cs.ds.config.whitelist.entrySet().stream().map(user -> "<li>" + user.getKey() + " <b>"
+ user.getValue().name + "</b> <small>" + user.getValue().lastUseDate + "</small>")
.collect(Collectors.joining("\n"));

final String url = "http://" + cs.ds.config.ipaddress + ":" + String.valueOf(httpPort);
String url = "http://" + cs.ds.config.ipaddress + ":" + String.valueOf(localDiscovery.getDefaultport());

final String reachable = discovery.selfTests().stream()
String reachable = localDiscovery.selfTests().stream()
.map(entry -> TR(TD(entry.address) + TD(toYesNo(entry.reachable)) + TD(toYesNo(entry.isOurs))))
.collect(Collectors.joining("\n"));

Registry registry = upnpService.getRegistry();
final String upnps;
String upnps;
if (registry != null) {
upnps = registry.getRemoteDevices().stream().map(device -> getDetails(device))
.map(details -> TR(TD(details.getSerialNumber()) + TD(details.getFriendlyName())))
Expand All @@ -165,7 +164,7 @@ public String getStatus() {
upnps = TR(TD("service not available") + TD(""));
}

if (!discovery.upnpAnnouncementThreadRunning()) {
if (!localDiscovery.upnpAnnouncementThreadRunning()) {
selfTestUpnpFound = upnpStatus.upnp_announcement_thread_not_running;
}

Expand Down Expand Up @@ -224,7 +223,9 @@ public void remoteDeviceUpdated(Registry registry, RemoteDevice device) {
@NonNullByDefault({})
@Override
public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
if (selfTestUpnpFound != upnpStatus.success || discovery.upnpAnnouncementThreadRunning()) {
UpnpServer localDiscovery = discovery;
if (selfTestUpnpFound != upnpStatus.success || localDiscovery == null
|| localDiscovery.upnpAnnouncementThreadRunning()) {
return;
}
DeviceDetails details = getDetails(device);
Expand All @@ -237,7 +238,9 @@ public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
@NonNullByDefault({})
@Override
public void localDeviceAdded(Registry registry, LocalDevice device) {
if (selfTestUpnpFound == upnpStatus.success || discovery.upnpAnnouncementThreadRunning()) {
UpnpServer localDiscovery = discovery;
if (selfTestUpnpFound == upnpStatus.success || localDiscovery == null
|| localDiscovery.upnpAnnouncementThreadRunning()) {
return;
}
checkForDevice(getDetails(device));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.io.hueemulation.internal.HueEmulationConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The upnp server runtime configuration. Based on a {@link HueEmulationConfig} and determined ip address and port.
Expand All @@ -33,6 +35,9 @@
*/
@NonNullByDefault
class HueEmulationConfigWithRuntime extends Thread implements Runnable {

private final Logger logger = LoggerFactory.getLogger(HueEmulationConfigWithRuntime.class);

final @NonNullByDefault({}) HueEmulationConfig config;
final InetAddress address;
final String addressString;
Expand Down Expand Up @@ -93,7 +98,8 @@ String getMulticastAddress() {
public synchronized CompletableFuture<@Nullable HueEmulationConfigWithRuntime> startNow(
@Nullable HueEmulationConfigWithRuntime ignored) {
if (hasAlreadyBeenStarted) {
throw new IllegalStateException("Cannot restart thread");
logger.debug("Cannot restart thread");
return future;
}
hasAlreadyBeenStarted = true;
super.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ private void useAddressPort(HueEmulationConfigWithRuntime r) {
* configuration ready event and depending on service start order we are also called by our own activate() method
* when the configuration is already ready at that time.
* <p>
* Therefore this method is "synchronized" and chains a completeable future for each call to re-evaluate the config
* Therefore this method is "synchronized" and chains a completable future for each call to re-evaluate the config
* after the former future has finished.
*/
@Override
Expand Down

0 comments on commit 51873b5

Please sign in to comment.