[neeo] fix ClassNotFoundException and switch to constructor injection (#9006)

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>
This commit is contained in:
J-N-K
2020-11-14 16:11:06 +01:00
committed by GitHub
parent 02341d3536
commit 1073f0086d
12 changed files with 172 additions and 353 deletions

View File

@@ -23,6 +23,7 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.ServletException;
import javax.ws.rs.client.ClientBuilder;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -82,19 +83,20 @@ public class NeeoService implements EventSubscriber, NetworkAddressChangeListene
* This is the context created in the activate method (and nulled in the deactivate method) that will provide the
* context to all services for all servlets
*/
private @Nullable ServiceContext context;
private final ServiceContext context;
// The following services are set by openHAB via the getter/setters
private @Nullable HttpService httpService;
private @Nullable ItemRegistry itemRegistry;
private @Nullable BindingInfoRegistry bindingInfoRegistry;
private @Nullable ThingRegistry thingRegistry;
private @Nullable ThingTypeRegistry thingTypeRegistry;
private @Nullable ItemChannelLinkRegistry itemChannelLinkRegistry;
private @Nullable ChannelTypeRegistry channelTypeRegistry;
private @Nullable MDNSClient mdnsClient;
private @Nullable EventPublisher eventPublisher;
private @Nullable NetworkAddressService networkAddressService;
private final HttpService httpService;
private final ItemRegistry itemRegistry;
private final BindingInfoRegistry bindingInfoRegistry;
private final ThingRegistry thingRegistry;
private final ThingTypeRegistry thingTypeRegistry;
private final ItemChannelLinkRegistry itemChannelLinkRegistry;
private final ChannelTypeRegistry channelTypeRegistry;
private final MDNSClient mdnsClient;
private final EventPublisher eventPublisher;
private final NetworkAddressService networkAddressService;
private final ClientBuilder clientBuilder;
/** The main dashboard servlet. Only created in the activate method (and disposed of in the deactivate method) */
private @Nullable NeeoDashboardServlet dashboardServlet;
@@ -136,238 +138,24 @@ public class NeeoService implements EventSubscriber, NetworkAddressChangeListene
}
};
/**
* The event filter to apply to this service
*/
private final EventFilter eventFilter = event -> {
logger.trace("apply: {}", event);
for (NeeoBrainServlet ns : servlets) {
final List<EventFilter> efs = ns.getEventFilters();
if (efs != null) {
for (EventFilter ef : efs) {
if (ef.apply(event)) {
logger.trace("apply (true): {}", event);
return true;
}
}
}
}
logger.trace("apply (false): {}", event);
return false;
};
/**
* Sets the http service.
*
* @param httpService the non-null http service
*/
@Reference
public void setHttpService(HttpService httpService) {
Objects.requireNonNull(httpService, "httpService cannot be null");
this.httpService = httpService;
}
/**
* Unset http service.
*
* @param httpService the http service (ignored)
*/
public void unsetHttpService(HttpService httpService) {
this.httpService = null;
}
/**
* Sets the item registry.
*
* @param itemRegistry the non-null item registry
*/
@Reference
public void setItemRegistry(ItemRegistry itemRegistry) {
Objects.requireNonNull(itemRegistry, "itemRegistry cannot be null");
this.itemRegistry = itemRegistry;
}
/**
* Unset item registry.
*
* @param itemRegistry the item registry (ignored)
*/
public void unsetItemRegistry(ItemRegistry itemRegistry) {
this.itemRegistry = null;
}
/**
* Sets the binding info registry.
*
* @param bindingInfoRegistry the non-null binding info registry
*/
@Reference
public void setBindingInfoRegistry(BindingInfoRegistry bindingInfoRegistry) {
Objects.requireNonNull(bindingInfoRegistry, "bindingInfoRegistry cannot be null");
this.bindingInfoRegistry = bindingInfoRegistry;
}
/**
* Unset binding info registry.
*
* @param bindingInfoRegistry the binding info registry (ignored)
*/
public void unsetBindingInfoRegistry(BindingInfoRegistry bindingInfoRegistry) {
this.bindingInfoRegistry = null;
}
/**
* Sets the thing registry.
*
* @param thingRegistry the non-null thing registry
*/
@Reference
public void setThingRegistry(ThingRegistry thingRegistry) {
Objects.requireNonNull(thingRegistry, "thingRegistry cannot be null");
this.thingRegistry = thingRegistry;
}
/**
* Unset thing registry.
*
* @param thingRegistry the thing registry (ignored)
*/
public void unsetThingRegistry(ThingRegistry thingRegistry) {
this.thingRegistry = null;
}
/**
* Sets the thing type registry.
*
* @param thingTypeRegistry the non-null thing type registry
*/
@Reference
public void setThingTypeRegistry(ThingTypeRegistry thingTypeRegistry) {
Objects.requireNonNull(thingTypeRegistry, "thingTypeRegistry cannot be null");
this.thingTypeRegistry = thingTypeRegistry;
}
/**
* Unset thing type registry.
*
* @param thingTypeRegistry the thing type registry (ignored)
*/
public void unsetThingTypeRegistry(ThingTypeRegistry thingTypeRegistry) {
this.thingTypeRegistry = null;
}
/**
* Sets the item channel link registry.
*
* @param itemChannelLinkRegistry the non-null item channel link registry
*/
@Reference
public void setItemChannelLinkRegistry(ItemChannelLinkRegistry itemChannelLinkRegistry) {
Objects.requireNonNull(itemChannelLinkRegistry, "itemChannelLinkRegistry cannot be null");
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
}
/**
* Unset item channel link registry.
*
* @param itemChannelLinkRegistry the item channel link registry (ignored)
*/
public void unsetItemChannelLinkRegistry(ItemChannelLinkRegistry itemChannelLinkRegistry) {
this.itemChannelLinkRegistry = null;
}
/**
* Sets the channel type registry.
*
* @param channelTypeRegistry the non-null channel type registry
*/
@Reference
public void setChannelTypeRegistry(ChannelTypeRegistry channelTypeRegistry) {
Objects.requireNonNull(channelTypeRegistry, "channelTypeRegistry cannot be null");
this.channelTypeRegistry = channelTypeRegistry;
}
/**
* Unset channel type registry.
*
* @param channelTypeRegistry the channel type registry (ignored)
*/
public void unsetChannelTypeRegistry(ChannelTypeRegistry channelTypeRegistry) {
this.channelTypeRegistry = null;
}
/**
* Sets the MDNS client.
*
* @param mdnsClient the non-null MDNS client
*/
@Reference
public void setMDNSClient(MDNSClient mdnsClient) {
Objects.requireNonNull(mdnsClient, "mdnsClient cannot be null");
this.mdnsClient = mdnsClient;
}
/**
* Unset MDNS client.
*
* @param mdnsClient the mdns client (ignored)
*/
public void unsetMDNSClient(MDNSClient mdnsClient) {
this.mdnsClient = null;
}
/**
* Sets the event publisher.
*
* @param eventPublisher the new event publisher
*/
@Reference
public void setEventPublisher(EventPublisher eventPublisher) {
Objects.requireNonNull(eventPublisher, "eventPublisher cannot be null");
this.eventPublisher = eventPublisher;
}
/**
* Unset event publisher.
*
* @param eventPublisher the event publisher (ignored)
*/
public void unsetEventPublisher(EventPublisher eventPublisher) {
this.eventPublisher = null;
}
/**
* Sets the network address service
*
* @param networkAddressService the network address service
*/
@Reference
public void setNetworkAddressService(NetworkAddressService networkAddressService) {
Objects.requireNonNull(networkAddressService, "networkAddressService cannot be null");
this.networkAddressService = networkAddressService;
networkAddressService.addNetworkAddressChangeListener(this);
}
/**
* Unsets network address service
*
* @param networkAddressService address service
*/
public void unsetNetworkAddressService(NetworkAddressService networkAddressService) {
networkAddressService.removeNetworkAddressChangeListener(this);
this.networkAddressService = null;
}
/**
* Activates this service. The activation will start up the brain discovery service and register the dashboard tile
*
* @param componentContext the non-null component context
*/
@Activate
public void activate(final ComponentContext componentContext) {
Objects.requireNonNull(componentContext, "componentContext cannot be null");
public NeeoService(ComponentContext componentContext, @Reference HttpService httpService,
@Reference ItemRegistry itemRegistry, @Reference ThingRegistry thingRegistry,
@Reference BindingInfoRegistry bindingInfoRegistry, @Reference ChannelTypeRegistry channelTypeRegistry,
@Reference ThingTypeRegistry thingTypeRegistry, @Reference ItemChannelLinkRegistry itemChannelLinkRegistry,
@Reference MDNSClient mdnsClient, @Reference EventPublisher eventPublisher,
@Reference NetworkAddressService networkAddressService, @Reference ClientBuilder clientBuilder) {
this.httpService = httpService;
this.itemRegistry = itemRegistry;
this.bindingInfoRegistry = bindingInfoRegistry;
this.channelTypeRegistry = channelTypeRegistry;
this.thingRegistry = thingRegistry;
this.thingTypeRegistry = thingTypeRegistry;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.mdnsClient = mdnsClient;
this.eventPublisher = eventPublisher;
this.networkAddressService = networkAddressService;
this.clientBuilder = clientBuilder;
logger.debug("Neeo Service activated");
final ServiceContext localContext = new ServiceContext(componentContext, validate(httpService, "httpService"),
@@ -378,7 +166,7 @@ public class NeeoService implements EventSubscriber, NetworkAddressChangeListene
validate(eventPublisher, "eventPublisher"), validate(networkAddressService, "networkAddressService"));
context = localContext;
discovery = new MdnsBrainDiscovery(localContext);
discovery = new MdnsBrainDiscovery(localContext, clientBuilder);
discovery.addListener(discoveryListener);
try {
@@ -402,6 +190,28 @@ public class NeeoService implements EventSubscriber, NetworkAddressChangeListene
}
}
/**
* The event filter to apply to this service
*/
private final EventFilter eventFilter = event -> {
logger.trace("apply: {}", event);
for (NeeoBrainServlet ns : servlets) {
final List<EventFilter> efs = ns.getEventFilters();
if (efs != null) {
for (EventFilter ef : efs) {
if (ef.apply(event)) {
logger.trace("apply (true): {}", event);
return true;
}
}
}
}
logger.trace("apply (false): {}", event);
return false;
};
/**
* Helper method to validate that the specific item wasn't null and convert it's type to a non-nullable type
*
@@ -441,8 +251,6 @@ public class NeeoService implements EventSubscriber, NetworkAddressChangeListene
NeeoUtil.close(servlet);
}
servlets.clear();
context = null;
}
if (dashboardServlet != null) {
@@ -472,7 +280,7 @@ public class NeeoService implements EventSubscriber, NetworkAddressChangeListene
servletUrl);
try {
final NeeoBrainServlet newServlet = NeeoBrainServlet.create(localContext, servletUrl,
sysInfo.getHostname(), ipAddress);
sysInfo.getHostname(), ipAddress, clientBuilder);
servlets.add(newServlet);
localContext.getHttpService().registerServlet(servletUrl, newServlet, new Hashtable<>(),

View File

@@ -27,6 +27,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.client.ClientBuilder;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -68,6 +70,8 @@ public class NeeoApi implements AutoCloseable {
/** The brain's IP address */
private final String brainIpAddress;
private final ClientBuilder clientBuilder;
/** The URL of the brain */
private final String brainUrl;
@@ -107,8 +111,7 @@ public class NeeoApi implements AutoCloseable {
private final AtomicReference<@Nullable ScheduledFuture<?>> checkStatus = new AtomicReference<>(null);
/** The {@link HttpRequest} used for making requests */
private final AtomicReference<HttpRequest> request = new AtomicReference<>(new HttpRequest());
private final AtomicReference<HttpRequest> request;
/** Whether the brain is currently connected */
private final AtomicBoolean connected = new AtomicBoolean(false);
@@ -120,21 +123,25 @@ public class NeeoApi implements AutoCloseable {
* @param context the non-null {@link ServiceContext}
* @throws IOException if an exception occurs connecting to the brain
*/
public NeeoApi(String ipAddress, String brainId, ServiceContext context) throws IOException {
public NeeoApi(String ipAddress, String brainId, ServiceContext context, ClientBuilder clientBuilder)
throws IOException {
NeeoUtil.requireNotEmpty(ipAddress, "ipAddress cannot be empty");
NeeoUtil.requireNotEmpty(brainId, "brainId cannot be empty");
Objects.requireNonNull(context, "context cannot be null");
this.brainIpAddress = ipAddress;
this.brainId = brainId;
this.clientBuilder = clientBuilder;
this.brainUrl = NeeoConstants.PROTOCOL + (ipAddress.startsWith("/") ? ipAddress.substring(1) : ipAddress) + ":"
+ NeeoConstants.DEFAULT_BRAIN_PORT;
deviceKeys = new NeeoDeviceKeys(brainUrl);
deviceKeys = new NeeoDeviceKeys(brainUrl, clientBuilder);
this.systemInfo = getSystemInfo(ipAddress);
request = new AtomicReference<>(new HttpRequest(clientBuilder));
this.systemInfo = getSystemInfo(ipAddress, clientBuilder);
String name = brainId;
try (HttpRequest request = new HttpRequest()) {
try (HttpRequest request = new HttpRequest(clientBuilder)) {
logger.debug("Getting existing device mappings from {}{}", brainUrl, NeeoConstants.PROJECTS_HOME);
final HttpResponse resp = request.sendGetCommand(brainUrl + NeeoConstants.PROJECTS_HOME);
if (resp.getHttpCode() != HttpStatus.OK_200) {
@@ -196,12 +203,12 @@ public class NeeoApi implements AutoCloseable {
* @return the non-null {@link NeeoSystemInfo} for the address
* @throws IOException Signals that an I/O exception has occurred or the URL is not a brain
*/
public static NeeoSystemInfo getSystemInfo(String ipAddress) throws IOException {
public static NeeoSystemInfo getSystemInfo(String ipAddress, ClientBuilder clientBuilder) throws IOException {
NeeoUtil.requireNotEmpty(ipAddress, "ipAddress cannot be empty");
final String sysInfo = NeeoConstants.PROTOCOL + (ipAddress.startsWith("/") ? ipAddress.substring(1) : ipAddress)
+ ":" + NeeoConstants.DEFAULT_BRAIN_PORT + NeeoConstants.SYSTEMINFO;
try (HttpRequest req = new HttpRequest()) {
try (HttpRequest req = new HttpRequest(clientBuilder)) {
final HttpResponse res = req.sendGetCommand(sysInfo);
if (res.getHttpCode() == HttpStatus.OK_200) {
return Objects.requireNonNull(GSON.fromJson(res.getContent(), NeeoSystemInfo.class));
@@ -387,7 +394,7 @@ public class NeeoApi implements AutoCloseable {
try {
setConnected(false);
NeeoUtil.close(request.getAndSet(new HttpRequest()));
NeeoUtil.close(request.getAndSet(new HttpRequest(clientBuilder)));
NeeoUtil.checkInterrupt();
registerApi();

View File

@@ -16,6 +16,8 @@ import java.io.IOException;
import java.net.InetAddress;
import java.util.Objects;
import javax.ws.rs.client.ClientBuilder;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.io.neeo.internal.models.BrainStatus;
import org.openhab.io.neeo.internal.servletservices.NeeoBrainSearchService;
@@ -44,8 +46,9 @@ public class NeeoBrainServlet extends AbstractServlet {
* @param servletUrl the non-null, non-empty servlet URL
* @param api the non-null API
*/
private NeeoBrainServlet(ServiceContext context, String servletUrl, NeeoApi api) {
super(context, servletUrl, new NeeoBrainSearchService(context), new NeeoBrainService(api, context));
private NeeoBrainServlet(ServiceContext context, String servletUrl, NeeoApi api, ClientBuilder clientBuilder) {
super(context, servletUrl, new NeeoBrainSearchService(context),
new NeeoBrainService(api, context, clientBuilder));
Objects.requireNonNull(context, "context cannot be null");
NeeoUtil.requireNotEmpty(servletUrl, "servletUrl cannot be empty");
@@ -65,16 +68,16 @@ public class NeeoBrainServlet extends AbstractServlet {
* @throws IOException when an exception occurs contacting the brain
*/
public static NeeoBrainServlet create(ServiceContext context, String servletUrl, String brainId,
InetAddress ipAddress) throws IOException {
InetAddress ipAddress, ClientBuilder clientBuilder) throws IOException {
Objects.requireNonNull(context, "context cannot be null");
NeeoUtil.requireNotEmpty(servletUrl, "servletUrl cannot be empty");
NeeoUtil.requireNotEmpty(brainId, "brainId cannot be empty");
Objects.requireNonNull(ipAddress, "ipAddress cannot be null");
final NeeoApi api = new NeeoApi(ipAddress.getHostAddress(), brainId, context);
final NeeoApi api = new NeeoApi(ipAddress.getHostAddress(), brainId, context, clientBuilder);
api.start();
return new NeeoBrainServlet(context, servletUrl, api);
return new NeeoBrainServlet(context, servletUrl, api, clientBuilder);
}
/**

View File

@@ -21,6 +21,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.client.ClientBuilder;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.http.HttpStatus;
@@ -52,15 +54,18 @@ public class NeeoDeviceKeys {
/** The brain's url */
private final String brainUrl;
private final ClientBuilder clientBuilder;
/**
* Creates the object from the context and brainUrl
*
* @param brainUrl the non-empty brain url
*/
NeeoDeviceKeys(String brainUrl) {
NeeoDeviceKeys(String brainUrl, ClientBuilder clientBuilder) {
NeeoUtil.requireNotEmpty(brainUrl, "brainUrl cannot be empty");
this.brainUrl = brainUrl;
this.clientBuilder = clientBuilder;
}
/**
@@ -69,7 +74,7 @@ public class NeeoDeviceKeys {
* @throws IOException Signals that an I/O exception has occurred.
*/
void refresh() throws IOException {
try (HttpRequest request = new HttpRequest()) {
try (HttpRequest request = new HttpRequest(clientBuilder)) {
logger.debug("Getting existing device mappings from {}{}", brainUrl, NeeoConstants.PROJECTS_HOME);
final HttpResponse resp = request.sendGetCommand(brainUrl + NeeoConstants.PROJECTS_HOME);
if (resp.getHttpCode() != HttpStatus.OK_200) {

View File

@@ -34,6 +34,7 @@ import java.util.stream.Collectors;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
import javax.ws.rs.client.ClientBuilder;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -106,14 +107,17 @@ public class MdnsBrainDiscovery extends AbstractBrainDiscovery {
/** The file we store definitions in */
private final File file = new File(NeeoConstants.FILENAME_DISCOVEREDBRAINS);
private final ClientBuilder clientBuilder;
/**
* Creates the MDNS brain discovery from the given {@link ServiceContext}
*
* @param context the non-null service context
*/
public MdnsBrainDiscovery(ServiceContext context) {
public MdnsBrainDiscovery(ServiceContext context, ClientBuilder clientBuilder) {
Objects.requireNonNull(context, "context cannot be null");
this.context = context;
this.clientBuilder = clientBuilder;
}
/**
@@ -250,7 +254,7 @@ public class MdnsBrainDiscovery extends AbstractBrainDiscovery {
NeeoSystemInfo sysInfo;
try {
sysInfo = NeeoApi.getSystemInfo(brainInfo.getValue().toString());
sysInfo = NeeoApi.getSystemInfo(brainInfo.getValue().toString(), clientBuilder);
} catch (IOException e) {
// We can get an MDNS notification BEFORE the brain is ready to process.
// if that happens, we'll get an IOException (usually bad gateway message), schedule another attempt to get
@@ -299,7 +303,7 @@ public class MdnsBrainDiscovery extends AbstractBrainDiscovery {
try {
final InetAddress addr = InetAddress.getByName(ipAddress);
final NeeoSystemInfo sysInfo = NeeoApi.getSystemInfo(ipAddress);
final NeeoSystemInfo sysInfo = NeeoApi.getSystemInfo(ipAddress, clientBuilder);
logger.debug("Manually adding brain ({}) with system information: {}", ipAddress, sysInfo);
systemsLock.lock();

View File

@@ -47,8 +47,8 @@ public class HttpRequest implements AutoCloseable {
/**
* Instantiates a new request
*/
public HttpRequest() {
client = ClientBuilder.newClient();
public HttpRequest(ClientBuilder clientBuilder) {
client = clientBuilder.build();
if (logger.isDebugEnabled()) {
client.register(new LoggingFilter(new Slf4LoggingAdapter(logger), true));

View File

@@ -21,6 +21,7 @@ import java.util.concurrent.ScheduledExecutorService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.client.ClientBuilder;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNull;
@@ -90,7 +91,7 @@ public class NeeoBrainService extends DefaultServletService {
private final ServiceContext context;
/** The HTTP request */
private final HttpRequest request = new HttpRequest();
private final HttpRequest request;
/** The scheduler to use to schedule recipe execution */
private final ScheduledExecutorService scheduler = ThreadPoolManager
@@ -114,7 +115,7 @@ public class NeeoBrainService extends DefaultServletService {
* @param api the non-null api
* @param context the non-null context
*/
public NeeoBrainService(NeeoApi api, ServiceContext context) {
public NeeoBrainService(NeeoApi api, ServiceContext context, ClientBuilder clientBuilder) {
Objects.requireNonNull(api, "api cannot be null");
Objects.requireNonNull(context, "context cannot be null");
@@ -125,6 +126,7 @@ public class NeeoBrainService extends DefaultServletService {
scheduler.execute(() -> {
resendState();
});
request = new HttpRequest(clientBuilder);
}
/**