[hueemulation] Replace Jersey dependency with JAX-RS Whiteboard (#8611)

Fixes #7647

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born 2020-09-30 03:53:43 +02:00 committed by GitHub
parent d72a077ba9
commit c5f87b44f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 322 additions and 311 deletions

View File

@ -14,10 +14,6 @@
<name>openHAB Add-ons :: Bundles :: IO :: Hue Emulation Service</name> <name>openHAB Add-ons :: Bundles :: IO :: Hue Emulation Service</name>
<properties>
<bnd.importpackage>org.glassfish.jersey.*;resolution:="optional"</bnd.importpackage>
</properties>
<dependencies> <dependencies>
<!-- https://mvnrepository.com/artifact/org.glassfish.grizzly/grizzly-http-server --> <!-- https://mvnrepository.com/artifact/org.glassfish.grizzly/grizzly-http-server -->
<dependency> <dependency>

View File

@ -71,8 +71,7 @@ import com.google.gson.GsonBuilder;
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { ConfigStore.class }, configurationPid = { @Component(immediate = false, service = ConfigStore.class, configurationPid = HueEmulationService.CONFIG_PID)
HueEmulationService.CONFIG_PID }, property = "com.eclipsesource.jaxrs.publish=false")
@ConfigurableService(category = "io", label = "Hue Emulation", description_uri = "io:hueemulation") @ConfigurableService(category = "io", label = "Hue Emulation", description_uri = "io:hueemulation")
@NonNullByDefault @NonNullByDefault
public class ConfigStore { public class ConfigStore {

View File

@ -14,9 +14,8 @@ package org.openhab.io.hueemulation.internal;
import java.util.Dictionary; import java.util.Dictionary;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Set;
import javax.servlet.ServletException;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.HttpMethod; import javax.ws.rs.HttpMethod;
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerRequestFilter;
@ -28,10 +27,6 @@ import javax.ws.rs.core.Application;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.servlet.ServletProperties;
import org.openhab.io.hueemulation.internal.rest.ConfigurationAccess; import org.openhab.io.hueemulation.internal.rest.ConfigurationAccess;
import org.openhab.io.hueemulation.internal.rest.LightsAndGroups; import org.openhab.io.hueemulation.internal.rest.LightsAndGroups;
import org.openhab.io.hueemulation.internal.rest.Rules; import org.openhab.io.hueemulation.internal.rest.Rules;
@ -42,6 +37,7 @@ import org.openhab.io.hueemulation.internal.rest.StatusResource;
import org.openhab.io.hueemulation.internal.rest.UserManagement; import org.openhab.io.hueemulation.internal.rest.UserManagement;
import org.openhab.io.hueemulation.internal.upnp.UpnpServer; import org.openhab.io.hueemulation.internal.upnp.UpnpServer;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration; import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
@ -54,8 +50,8 @@ import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin; import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler; import org.osgi.service.event.EventHandler;
import org.osgi.service.http.HttpService; import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.http.NamespaceException; import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -75,17 +71,12 @@ import org.slf4j.LoggerFactory;
* @author David Graeff - Initial Contribution * @author David Graeff - Initial Contribution
*/ */
@NonNullByDefault @NonNullByDefault
@Component(immediate = true, service = { HueEmulationService.class }, property = { @Component(immediate = true, service = HueEmulationService.class)
"com.eclipsesource.jaxrs.publish=false" })
public class HueEmulationService implements EventHandler { public class HueEmulationService implements EventHandler {
public static final String CONFIG_PID = "org.openhab.hueemulation"; public static final String CONFIG_PID = "org.openhab.hueemulation";
public static final String RESTAPI_PATH = "/api"; public static final String RESTAPI_PATH = "/api";
public static final String REST_APP_NAME = "HueEmulation";
@ApplicationPath(RESTAPI_PATH)
public static class JerseyApplication extends Application {
}
@PreMatching @PreMatching
public class RequestInterceptor implements ContainerRequestFilter { public class RequestInterceptor implements ContainerRequestFilter {
@ -117,6 +108,34 @@ public class HueEmulationService implements EventHandler {
} }
private final ContainerRequestFilter requestCleaner = new RequestInterceptor(); private final ContainerRequestFilter requestCleaner = new RequestInterceptor();
/**
* The Jax-RS application that starts up all REST activities.
* It registers itself as a Jax-RS Whiteboard service and all Jax-RS resources that are targeting REST_APP_NAME will
* start up.
*/
@JaxrsName(REST_APP_NAME)
private class RESTapplication extends Application {
private String root;
RESTapplication(String root) {
this.root = root;
}
@NonNullByDefault({})
@Override
public Set<Object> getSingletons() {
return Set.of(userManagement, configurationAccess, lightItems, sensors, scenes, schedules, rules,
statusResource, accessInterceptor);
}
Dictionary<String, String> serviceProperties() {
Dictionary<String, String> dict = new Hashtable<>();
dict.put(JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE, root);
return dict;
}
}
private final Logger logger = LoggerFactory.getLogger(HueEmulationService.class); private final Logger logger = LoggerFactory.getLogger(HueEmulationService.class);
private final LogAccessInterceptor accessInterceptor = new LogAccessInterceptor(); private final LogAccessInterceptor accessInterceptor = new LogAccessInterceptor();
@ -144,9 +163,8 @@ public class HueEmulationService implements EventHandler {
@Reference @Reference
protected @NonNullByDefault({}) StatusResource statusResource; protected @NonNullByDefault({}) StatusResource statusResource;
@Reference private @Nullable ServiceRegistration<?> eventHandler;
protected @NonNullByDefault({}) HttpService httpService; private @Nullable ServiceRegistration<Application> restService;
private @NonNullByDefault({}) ServiceRegistration<?> eventHandler;
@Activate @Activate
protected void activate(BundleContext bc) { protected void activate(BundleContext bc) {
@ -165,15 +183,11 @@ public class HueEmulationService implements EventHandler {
@Deactivate @Deactivate
protected void deactivate() { protected void deactivate() {
try { unregisterEventHandler();
if (eventHandler != null) {
eventHandler.unregister(); ServiceRegistration<Application> localRestService = restService;
} if (localRestService != null) {
} catch (IllegalStateException ignore) { localRestService.unregister();
}
try {
httpService.unregister(RESTAPI_PATH);
} catch (IllegalArgumentException ignore) {
} }
} }
@ -185,39 +199,26 @@ public class HueEmulationService implements EventHandler {
*/ */
@Override @Override
public void handleEvent(@Nullable Event event) { public void handleEvent(@Nullable Event event) {
try { // Only receive this event once unregisterEventHandler();
eventHandler.unregister();
eventHandler = null;
} catch (IllegalStateException ignore) {
}
ResourceConfig resourceConfig = ResourceConfig.forApplicationClass(JerseyApplication.class); ServiceRegistration<Application> localRestService = restService;
resourceConfig.property(ServerProperties.APPLICATION_NAME, "HueEmulation"); if (localRestService == null) {
// don't look for implementations described by META-INF/services/* RESTapplication app = new RESTapplication(RESTAPI_PATH);
resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true); BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
// disable auto discovery on server, as it's handled via OSGI restService = context.registerService(Application.class, app, app.serviceProperties());
resourceConfig.property(ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true);
resourceConfig.property(ServerProperties.PROCESSING_RESPONSE_ERRORS_ENABLED, true);
resourceConfig.registerInstances(userManagement, configurationAccess, lightItems, sensors, scenes, schedules,
rules, statusResource, accessInterceptor, requestCleaner);
try {
Hashtable<String, String> initParams = new Hashtable<>();
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);
UpnpServer localDiscovery = discovery;
if (localDiscovery == null) {
logger.warn("The UPnP Server service has not been started!");
} else if (!localDiscovery.upnpAnnouncementThreadRunning()) {
localDiscovery.handleEvent(null);
}
statusResource.startUpnpSelfTest();
logger.info("Hue Emulation service available under {}", RESTAPI_PATH); logger.info("Hue Emulation service available under {}", RESTAPI_PATH);
} catch (ServletException | NamespaceException e) { }
logger.warn("Could not start Hue Emulation service: {}", e.getMessage(), e); }
private void unregisterEventHandler() {
ServiceRegistration<?> localEventHandler = eventHandler;
if (localEventHandler != null) {
try {
localEventHandler.unregister();
eventHandler = null;
} catch (IllegalStateException e) {
logger.debug("EventHandler already unregistered", e);
}
} }
} }
} }

View File

@ -79,7 +79,7 @@ public class HueAuthorizedConfig extends HueUnauthorizedConfig {
/** /**
* Return a json serializer that behaves like the default one, but updates the UTC and localtime fields * Return a json serializer that behaves like the default one, but updates the UTC and localtime fields
* before each serializion. * before each serialization.
*/ */
@NonNullByDefault({}) @NonNullByDefault({})
public static class Serializer implements JsonSerializer<HueAuthorizedConfig> { public static class Serializer implements JsonSerializer<HueAuthorizedConfig> {

View File

@ -24,11 +24,11 @@ import org.openhab.core.library.types.PercentType;
public class HueStateBulb extends HueStatePlug { public class HueStateBulb extends HueStatePlug {
// https://github.com/openhab/openhab-addons/issues/2881 // https://github.com/openhab/openhab-addons/issues/2881
// Apparently the maximum brightness is 254 // Apparently the maximum brightness is 254
public static int MAX_BRI = 254; public static final int MAX_BRI = 254;
public int bri = 0; public int bri = 0;
/** white color temperature, 154 (cold) - 500 (warm) */ /** white color temperature, 154 (cold) - 500 (warm) */
public static int MAX_CT = 500; public static final int MAX_CT = 500;
public int ct = 500; public int ct = 500;
protected HueStateBulb() { protected HueStateBulb() {

View File

@ -25,9 +25,9 @@ import org.openhab.core.library.types.PercentType;
* *
*/ */
public class HueStateColorBulb extends HueStateBulb { public class HueStateColorBulb extends HueStateBulb {
public static int MAX_HUE = 65535; // For extended color light bulbs public static final int MAX_HUE = 65535; // For extended color light bulbs
public int hue = 0; public int hue = 0;
public static int MAX_SAT = 254; public static final int MAX_SAT = 254;
public int sat = 0; public int sat = 0;
// color as array of xy-coordinates // color as array of xy-coordinates

View File

@ -15,7 +15,7 @@ package org.openhab.io.hueemulation.internal.dto.changerequest;
import java.util.List; import java.util.List;
/** /**
* A POST message on a light entpoint will contain this change message body. * A POST message on a light endpoint will contain this change message body.
* Not all fields will be set and always need to be checked. * Not all fields will be set and always need to be checked.
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution

View File

@ -27,6 +27,7 @@ import javax.ws.rs.core.UriInfo;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.NetworkUtils; import org.openhab.io.hueemulation.internal.NetworkUtils;
import org.openhab.io.hueemulation.internal.dto.HueUnauthorizedConfig; import org.openhab.io.hueemulation.internal.dto.HueUnauthorizedConfig;
import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeRequest; import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeRequest;
@ -35,19 +36,22 @@ import org.openhab.io.hueemulation.internal.dto.response.HueResponse.HueErrorMes
import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.annotations.ApiResponses;
/** /**
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { @Component(immediate = false, service = ConfigurationAccess.class)
ConfigurationAccess.class }, property = "com.eclipsesource.jaxrs.publish=false") @JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("") @Path("")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -62,8 +66,8 @@ public class ConfigurationAccess {
@GET @GET
@Path("config") @Path("config")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the reduced configuration") @Operation(summary = "Return the reduced configuration", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response getReducedConfigApi() { public Response getReducedConfigApi() {
return Response.ok(cs.gson.toJson(cs.ds.config, new TypeToken<HueUnauthorizedConfig>() { return Response.ok(cs.gson.toJson(cs.ds.config, new TypeToken<HueUnauthorizedConfig>() {
}.getType())).build(); }.getType())).build();
@ -72,10 +76,10 @@ public class ConfigurationAccess {
@GET @GET
@Path("{username}") @Path("{username}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the full data store") @Operation(summary = "Return the full data store", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response getAllApi(@Context UriInfo uri, public Response getAllApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -85,10 +89,10 @@ public class ConfigurationAccess {
@GET @GET
@Path("{username}/config") @Path("{username}/config")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the configuration") @Operation(summary = "Return the configuration", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response getFullConfigApi(@Context UriInfo uri, public Response getFullConfigApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -98,10 +102,10 @@ public class ConfigurationAccess {
@PUT @PUT
@Path("{username}/config") @Path("{username}/config")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the reduced configuration") @Operation(summary = "Return the reduced configuration", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response putFullConfigApi(@Context UriInfo uri, public Response putFullConfigApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, String body) { @PathParam("username") @Parameter(description = "username") String username, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }

View File

@ -43,6 +43,7 @@ import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.DeviceType; import org.openhab.io.hueemulation.internal.DeviceType;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.NetworkUtils; import org.openhab.io.hueemulation.internal.NetworkUtils;
import org.openhab.io.hueemulation.internal.StateUtils; import org.openhab.io.hueemulation.internal.StateUtils;
import org.openhab.io.hueemulation.internal.dto.HueGroupEntry; import org.openhab.io.hueemulation.internal.dto.HueGroupEntry;
@ -57,15 +58,17 @@ import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.annotations.ApiResponses;
/** /**
* Listens to the ItemRegistry for items that fulfill one of these criteria: * Listens to the ItemRegistry for items that fulfill one of these criteria:
@ -93,7 +96,9 @@ import io.swagger.annotations.ApiResponses;
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
* @author Florian Schmidt - Removed base type restriction from Group items * @author Florian Schmidt - Removed base type restriction from Group items
*/ */
@Component(immediate = false, service = { LightsAndGroups.class }, property = "com.eclipsesource.jaxrs.publish=false") @Component(immediate = false, service = LightsAndGroups.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("") @Path("")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -241,10 +246,9 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/lights") @Path("{username}/lights")
@ApiOperation(value = "Return all lights") @Operation(summary = "Return all lights", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getAllLightsApi(@Context UriInfo uri, public Response getAllLightsApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -253,10 +257,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/lights/new") @Path("{username}/lights/new")
@ApiOperation(value = "Return new lights since last scan. Returns an empty list for openHAB as we do not cache that information.") @Operation(summary = "Return new lights since last scan. Returns an empty list for openHAB as we do not cache that information.", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response getNewLights(@Context UriInfo uri, public Response getNewLights(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -265,10 +269,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@POST @POST
@Path("{username}/lights") @Path("{username}/lights")
@ApiOperation(value = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.") @Operation(summary = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response postNewLights(@Context UriInfo uri, public Response postNewLights(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -277,11 +281,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/lights/{id}") @Path("{username}/lights/{id}")
@ApiOperation(value = "Return a light") @Operation(summary = "Return a light", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getLightApi(@Context UriInfo uri, // public Response getLightApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id) { @PathParam("id") @Parameter(description = "light id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -291,12 +294,12 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@DELETE @DELETE
@Path("{username}/lights/{id}") @Path("{username}/lights/{id}")
@ApiOperation(value = "Deletes the item that is represented by this id") @Operation(summary = "Deletes the item that is represented by this id", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"), @ApiResponse(responseCode = "200", description = "The item got removed"),
@ApiResponse(code = 403, message = "Access denied") }) @ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeLightAPI(@Context UriInfo uri, public Response removeLightAPI(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "id") String id) { @PathParam("id") @Parameter(description = "id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -316,11 +319,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@PUT @PUT
@Path("{username}/lights/{id}") @Path("{username}/lights/{id}")
@ApiOperation(value = "Rename a light") @Operation(summary = "Rename a light", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response renameLightApi(@Context UriInfo uri, // public Response renameLightApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id, String body) { @PathParam("id") @Parameter(description = "light id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -345,11 +347,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@PUT @PUT
@Path("{username}/lights/{id}/state") @Path("{username}/lights/{id}/state")
@ApiOperation(value = "Set light state") @Operation(summary = "Set light state", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response setLightStateApi(@Context UriInfo uri, // public Response setLightStateApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id, String body) { @PathParam("id") @Parameter(description = "light id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -391,11 +392,11 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@PUT @PUT
@Path("{username}/groups/{id}/action") @Path("{username}/groups/{id}/action")
@ApiOperation(value = "Initiate group action") @Operation(summary = "Initiate group action", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response setGroupActionApi(@Context UriInfo uri, // public Response setGroupActionApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "group id") String id, String body) { @PathParam("id") @Parameter(description = "group id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -436,10 +437,9 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/groups") @Path("{username}/groups")
@ApiOperation(value = "Return all groups") @Operation(summary = "Return all groups", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getAllGroupsApi(@Context UriInfo uri, public Response getAllGroupsApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -448,11 +448,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/groups/{id}") @Path("{username}/groups/{id}")
@ApiOperation(value = "Return a group") @Operation(summary = "Return a group", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getGroupApi(@Context UriInfo uri, // public Response getGroupApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "group id") String id) { @PathParam("id") @Parameter(description = "group id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -462,10 +461,9 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@POST @POST
@Path("{username}/groups") @Path("{username}/groups")
@ApiOperation(value = "Create a new group") @Operation(summary = "Create a new group", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response postNewGroup(@Context UriInfo uri, public Response postNewGroup(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, String body) { @PathParam("username") @Parameter(description = "username") String username, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -506,12 +504,12 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@DELETE @DELETE
@Path("{username}/groups/{id}") @Path("{username}/groups/{id}")
@ApiOperation(value = "Deletes the item that is represented by this id") @Operation(summary = "Deletes the item that is represented by this id", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"), @ApiResponse(responseCode = "200", description = "The item got removed"),
@ApiResponse(code = 403, message = "Access denied") }) @ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeGroupAPI(@Context UriInfo uri, public Response removeGroupAPI(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "id") String id) { @PathParam("id") @Parameter(description = "id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }

View File

@ -49,6 +49,7 @@ import org.openhab.core.config.core.Configuration;
import org.openhab.core.items.Item; import org.openhab.core.items.Item;
import org.openhab.core.items.ItemRegistry; import org.openhab.core.items.ItemRegistry;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.NetworkUtils; import org.openhab.io.hueemulation.internal.NetworkUtils;
import org.openhab.io.hueemulation.internal.RuleUtils; import org.openhab.io.hueemulation.internal.RuleUtils;
import org.openhab.io.hueemulation.internal.dto.HueRuleEntry; import org.openhab.io.hueemulation.internal.dto.HueRuleEntry;
@ -59,18 +60,22 @@ import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.annotations.ApiResponses;
/** /**
* Handles Hue rules via the automation subsystem and the corresponding REST interface * Handles Hue rules via the automation subsystem and the corresponding REST interface
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { Rules.class }, property = "com.eclipsesource.jaxrs.publish=false") @Component(immediate = false, service = Rules.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("") @Path("")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -234,10 +239,9 @@ public class Rules implements RegistryChangeListener<Rule> {
@GET @GET
@Path("{username}/rules") @Path("{username}/rules")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return all rules") @Operation(summary = "Return all rules", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getRulesApi(@Context UriInfo uri, public Response getRulesApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -246,11 +250,10 @@ public class Rules implements RegistryChangeListener<Rule> {
@GET @GET
@Path("{username}/rules/{id}") @Path("{username}/rules/{id}")
@ApiOperation(value = "Return a rule") @Operation(summary = "Return a rule", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getRuleApi(@Context UriInfo uri, // public Response getRuleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "rule id") String id) { @PathParam("id") @Parameter(description = "rule id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -259,12 +262,12 @@ public class Rules implements RegistryChangeListener<Rule> {
@DELETE @DELETE
@Path("{username}/rules/{id}") @Path("{username}/rules/{id}")
@ApiOperation(value = "Deletes a rule") @Operation(summary = "Deletes a rule", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), @ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") }) @ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeRuleApi(@Context UriInfo uri, public Response removeRuleApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "Rule to remove") String id) { @PathParam("id") @Parameter(description = "Rule to remove") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -279,11 +282,10 @@ public class Rules implements RegistryChangeListener<Rule> {
@PUT @PUT
@Path("{username}/rules/{id}") @Path("{username}/rules/{id}")
@ApiOperation(value = "Set rule attributes") @Operation(summary = "Set rule attributes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response modifyRuleApi(@Context UriInfo uri, // public Response modifyRuleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "rule id") String id, String body) { @PathParam("id") @Parameter(description = "rule id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -334,10 +336,9 @@ public class Rules implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "null" }) @SuppressWarnings({ "null" })
@POST @POST
@Path("{username}/rules") @Path("{username}/rules")
@ApiOperation(value = "Create a new rule") @Operation(summary = "Create a new rule", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response postNewRule(@Context UriInfo uri, public Response postNewRule(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, String body) { @PathParam("username") @Parameter(description = "username") String username, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }

View File

@ -49,6 +49,7 @@ import org.openhab.core.items.ItemRegistry;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.State; import org.openhab.core.types.State;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.NetworkUtils; import org.openhab.io.hueemulation.internal.NetworkUtils;
import org.openhab.io.hueemulation.internal.StateUtils; import org.openhab.io.hueemulation.internal.StateUtils;
import org.openhab.io.hueemulation.internal.automation.dto.ItemCommandActionConfig; import org.openhab.io.hueemulation.internal.automation.dto.ItemCommandActionConfig;
@ -63,20 +64,24 @@ import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.annotations.ApiResponses;
/** /**
* Handles Hue scenes via the automation subsystem and the corresponding REST interface * Handles Hue scenes via the automation subsystem and the corresponding REST interface
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { Scenes.class }, property = "com.eclipsesource.jaxrs.publish=false") @Component(immediate = false, service = Scenes.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("") @Path("")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -165,10 +170,9 @@ public class Scenes implements RegistryChangeListener<Rule> {
@GET @GET
@Path("{username}/scenes") @Path("{username}/scenes")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return all scenes") @Operation(summary = "Return all scenes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getScenesApi(@Context UriInfo uri, public Response getScenesApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -178,11 +182,10 @@ public class Scenes implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "unused", "null" }) @SuppressWarnings({ "unused", "null" })
@GET @GET
@Path("{username}/scenes/{id}") @Path("{username}/scenes/{id}")
@ApiOperation(value = "Return a scene") @Operation(summary = "Return a scene", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getSceneApi(@Context UriInfo uri, // public Response getSceneApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "scene id") String id) { @PathParam("id") @Parameter(description = "scene id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -208,12 +211,12 @@ public class Scenes implements RegistryChangeListener<Rule> {
@DELETE @DELETE
@Path("{username}/scenes/{id}") @Path("{username}/scenes/{id}")
@ApiOperation(value = "Deletes a scene") @Operation(summary = "Deletes a scene", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), @ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") }) @ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeSceneApi(@Context UriInfo uri, public Response removeSceneApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "Scene to remove") String id) { @PathParam("id") @Parameter(description = "Scene to remove") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -248,11 +251,10 @@ public class Scenes implements RegistryChangeListener<Rule> {
*/ */
@PUT @PUT
@Path("{username}/scenes/{id}") @Path("{username}/scenes/{id}")
@ApiOperation(value = "Set scene attributes") @Operation(summary = "Set scene attributes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response modifySceneApi(@Context UriInfo uri, // public Response modifySceneApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "scene id") String id, String body) { @PathParam("id") @Parameter(description = "scene id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -317,10 +319,9 @@ public class Scenes implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "null" }) @SuppressWarnings({ "null" })
@POST @POST
@Path("{username}/scenes") @Path("{username}/scenes")
@ApiOperation(value = "Create a new scene") @Operation(summary = "Create a new scene", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response postNewScene(@Context UriInfo uri, public Response postNewScene(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, String body) { @PathParam("username") @Parameter(description = "username") String username, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -372,12 +373,11 @@ public class Scenes implements RegistryChangeListener<Rule> {
@PUT @PUT
@Path("{username}/scenes/{id}/lightstates/{lightid}") @Path("{username}/scenes/{id}/lightstates/{lightid}")
@ApiOperation(value = "Set scene attributes") @Operation(summary = "Set scene attributes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response modifySceneLightStateApi(@Context UriInfo uri, // public Response modifySceneLightStateApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "scene id") String id, @PathParam("id") @Parameter(description = "scene id") String id,
@PathParam("lightid") @ApiParam(value = "light id") String lightid, String body) { @PathParam("lightid") @Parameter(description = "light id") String lightid, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }

View File

@ -44,6 +44,7 @@ import org.openhab.core.automation.util.RuleBuilder;
import org.openhab.core.common.registry.RegistryChangeListener; import org.openhab.core.common.registry.RegistryChangeListener;
import org.openhab.core.config.core.Configuration; import org.openhab.core.config.core.Configuration;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.NetworkUtils; import org.openhab.io.hueemulation.internal.NetworkUtils;
import org.openhab.io.hueemulation.internal.RuleUtils; import org.openhab.io.hueemulation.internal.RuleUtils;
import org.openhab.io.hueemulation.internal.dto.HueDataStore; import org.openhab.io.hueemulation.internal.dto.HueDataStore;
@ -56,13 +57,15 @@ import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.annotations.ApiResponses;
/** /**
* Enables the schedule part of the Hue REST API. Uses automation rules with GenericCronTrigger, TimerTrigger and * Enables the schedule part of the Hue REST API. Uses automation rules with GenericCronTrigger, TimerTrigger and
@ -74,7 +77,9 @@ import io.swagger.annotations.ApiResponses;
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { Schedules.class }, property = "com.eclipsesource.jaxrs.publish=false") @Component(immediate = false, service = Schedules.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("") @Path("")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -228,10 +233,9 @@ public class Schedules implements RegistryChangeListener<Rule> {
@GET @GET
@Path("{username}/schedules") @Path("{username}/schedules")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return all schedules") @Operation(summary = "Return all schedules", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getSchedulesApi(@Context UriInfo uri, public Response getSchedulesApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -240,11 +244,10 @@ public class Schedules implements RegistryChangeListener<Rule> {
@GET @GET
@Path("{username}/schedules/{id}") @Path("{username}/schedules/{id}")
@ApiOperation(value = "Return a schedule") @Operation(summary = "Return a schedule", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getScheduleApi(@Context UriInfo uri, // public Response getScheduleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "schedule id") String id) { @PathParam("id") @Parameter(description = "schedule id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -253,12 +256,12 @@ public class Schedules implements RegistryChangeListener<Rule> {
@DELETE @DELETE
@Path("{username}/schedules/{id}") @Path("{username}/schedules/{id}")
@ApiOperation(value = "Deletes a schedule") @Operation(summary = "Deletes a schedule", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), @ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") }) @ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeScheduleApi(@Context UriInfo uri, public Response removeScheduleApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "Schedule to remove") String id) { @PathParam("id") @Parameter(description = "Schedule to remove") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -273,11 +276,11 @@ public class Schedules implements RegistryChangeListener<Rule> {
@PUT @PUT
@Path("{username}/schedules/{id}") @Path("{username}/schedules/{id}")
@ApiOperation(value = "Set schedule attributes") @Operation(summary = "Set schedule attributes", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response modifyScheduleApi(@Context UriInfo uri, // public Response modifyScheduleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "schedule id") String id, String body) { @PathParam("id") @Parameter(description = "schedule id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -311,10 +314,10 @@ public class Schedules implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "null" }) @SuppressWarnings({ "null" })
@POST @POST
@Path("{username}/schedules") @Path("{username}/schedules")
@ApiOperation(value = "Create a new schedule") @Operation(summary = "Create a new schedule", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response postNewSchedule(@Context UriInfo uri, public Response postNewSchedule(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, String body) { @PathParam("username") @Parameter(description = "username") String username, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }

View File

@ -36,6 +36,7 @@ import org.openhab.core.items.Item;
import org.openhab.core.items.ItemRegistry; import org.openhab.core.items.ItemRegistry;
import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.CoreItemFactory;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.NetworkUtils; import org.openhab.io.hueemulation.internal.NetworkUtils;
import org.openhab.io.hueemulation.internal.dto.HueNewLights; import org.openhab.io.hueemulation.internal.dto.HueNewLights;
import org.openhab.io.hueemulation.internal.dto.HueSensorEntry; import org.openhab.io.hueemulation.internal.dto.HueSensorEntry;
@ -45,13 +46,15 @@ import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.annotations.ApiResponses;
/** /**
* Listens to the ItemRegistry and add all DecimalType, OnOffType, ContactType, DimmerType items * Listens to the ItemRegistry and add all DecimalType, OnOffType, ContactType, DimmerType items
@ -59,7 +62,9 @@ import io.swagger.annotations.ApiResponses;
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { Sensors.class }, property = "com.eclipsesource.jaxrs.publish=false") @Component(immediate = false, service = Sensors.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("") @Path("")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -144,10 +149,9 @@ public class Sensors implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/sensors") @Path("{username}/sensors")
@ApiOperation(value = "Return all sensors") @Operation(summary = "Return all sensors", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getAllSensorsApi(@Context UriInfo uri, public Response getAllSensorsApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -156,10 +160,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/sensors/new") @Path("{username}/sensors/new")
@ApiOperation(value = "Return new sensors since last scan. Returns an empty list for openHAB as we do not cache that information.") @Operation(summary = "Return new sensors since last scan. Returns an empty list for openHAB as we do not cache that information.", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response getNewSensors(@Context UriInfo uri, public Response getNewSensors(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -168,10 +172,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@POST @POST
@Path("{username}/sensors") @Path("{username}/sensors")
@ApiOperation(value = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.") @Operation(summary = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response postNewLights(@Context UriInfo uri, public Response postNewLights(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -180,11 +184,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@GET @GET
@Path("{username}/sensors/{id}") @Path("{username}/sensors/{id}")
@ApiOperation(value = "Return a sensor") @Operation(summary = "Return a sensor", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getSensorApi(@Context UriInfo uri, // public Response getSensorApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "sensor id") String id) { @PathParam("id") @Parameter(description = "sensor id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -194,11 +197,11 @@ public class Sensors implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@GET @GET
@Path("{username}/sensors/{id}/config") @Path("{username}/sensors/{id}/config")
@ApiOperation(value = "Return a sensor config. Always empty") @Operation(summary = "Return a sensor config. Always empty", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) @ApiResponse(responseCode = "200", description = "OK") })
public Response getSensorConfigApi(@Context UriInfo uri, // public Response getSensorConfigApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "sensor id") String id) { @PathParam("id") @Parameter(description = "sensor id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -214,12 +217,12 @@ public class Sensors implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@DELETE @DELETE
@Path("{username}/sensors/{id}") @Path("{username}/sensors/{id}")
@ApiOperation(value = "Deletes the sensor that is represented by this id") @Operation(summary = "Deletes the sensor that is represented by this id", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"), @ApiResponse(responseCode = "200", description = "The item got removed"),
@ApiResponse(code = 403, message = "Access denied") }) @ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeSensorAPI(@Context UriInfo uri, public Response removeSensorAPI(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "id") String id) { @PathParam("id") @Parameter(description = "id") String id) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -239,11 +242,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" }) @SuppressWarnings({ "null", "unused" })
@PUT @PUT
@Path("{username}/sensors/{id}") @Path("{username}/sensors/{id}")
@ApiOperation(value = "Rename a sensor") @Operation(summary = "Rename a sensor", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response renameLightApi(@Context UriInfo uri, // public Response renameLightApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id, String body) { @PathParam("id") @Parameter(description = "light id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -267,11 +269,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@PUT @PUT
@Path("{username}/sensors/{id}/state") @Path("{username}/sensors/{id}/state")
@ApiOperation(value = "Set sensor state") @Operation(summary = "Set sensor state", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response setSensorStateApi(@Context UriInfo uri, // public Response setSensorStateApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "sensor id") String id, String body) { @PathParam("id") @Parameter(description = "sensor id") String id, String body) {
if (!userManagement.authorizeUser(username)) { if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }

View File

@ -33,11 +33,15 @@ import org.jupnp.model.meta.RemoteDevice;
import org.jupnp.registry.Registry; import org.jupnp.registry.Registry;
import org.jupnp.registry.RegistryListener; import org.jupnp.registry.RegistryListener;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.upnp.UpnpServer; import org.openhab.io.hueemulation.internal.upnp.UpnpServer;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -49,9 +53,11 @@ import org.slf4j.LoggerFactory;
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { StatusResource.class }, property = "com.eclipsesource.jaxrs.publish=false") @Component(immediate = false, service = StatusResource.class)
@Path("") @JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("")
public class StatusResource implements RegistryListener { public class StatusResource implements RegistryListener {
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY) @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
protected @Nullable UpnpServer discovery; protected @Nullable UpnpServer discovery;

View File

@ -33,6 +33,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.common.registry.DefaultAbstractManagedProvider; import org.openhab.core.common.registry.DefaultAbstractManagedProvider;
import org.openhab.core.storage.StorageService; import org.openhab.core.storage.StorageService;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.NetworkUtils; import org.openhab.io.hueemulation.internal.NetworkUtils;
import org.openhab.io.hueemulation.internal.dto.HueUserAuth; import org.openhab.io.hueemulation.internal.dto.HueUserAuth;
import org.openhab.io.hueemulation.internal.dto.HueUserAuthWithSecrets; import org.openhab.io.hueemulation.internal.dto.HueUserAuthWithSecrets;
@ -42,15 +43,17 @@ import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseCreat
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.annotations.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.annotations.ApiResponses;
/** /**
* Manages users of this emulated HUE bridge. Stores users in the frameworks storage backend. * Manages users of this emulated HUE bridge. Stores users in the frameworks storage backend.
@ -64,7 +67,9 @@ import io.swagger.annotations.ApiResponses;
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@Component(immediate = false, service = { UserManagement.class }, property = "com.eclipsesource.jaxrs.publish=false") @Component(immediate = false, service = UserManagement.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault @NonNullByDefault
@Path("") @Path("")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -147,9 +152,9 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
} }
@POST @POST
@ApiOperation(value = "Create an API Key") @Operation(summary = "Create an API Key", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "API Key created"), @ApiResponse(responseCode = "200", description = "API Key created"),
@ApiResponse(code = 403, message = "Link button not pressed") }) @ApiResponse(responseCode = "403", description = "Link button not pressed") })
public Response createNewUser(@Context UriInfo uri, String body) { public Response createNewUser(@Context UriInfo uri, String body) {
if (!cs.ds.config.linkbutton) { if (!cs.ds.config.linkbutton) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.LINK_BUTTON_NOT_PRESSED, return NetworkUtils.singleError(cs.gson, uri, HueResponse.LINK_BUTTON_NOT_PRESSED,
@ -175,11 +180,10 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
@GET @GET
@Path("{username}/config/whitelist/{userid}") @Path("{username}/config/whitelist/{userid}")
@ApiOperation(value = "Return a user") @Operation(summary = "Return a user", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getUserApi(@Context UriInfo uri, public Response getUserApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("userid") @ApiParam(value = "User ID") String userid) { @PathParam("userid") @Parameter(description = "User ID") String userid) {
if (!authorizeUser(username)) { if (!authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -188,10 +192,9 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
@GET @GET
@Path("{username}/config/whitelist") @Path("{username}/config/whitelist")
@ApiOperation(value = "Return all users") @Operation(summary = "Return all users", responses = { @ApiResponse(responseCode = "200", description = "OK") })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
public Response getAllUsersApi(@Context UriInfo uri, public Response getAllUsersApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) { @PathParam("username") @Parameter(description = "username") String username) {
if (!authorizeUser(username)) { if (!authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }
@ -200,12 +203,12 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
@DELETE @DELETE
@Path("{username}/config/whitelist/{id}") @Path("{username}/config/whitelist/{id}")
@ApiOperation(value = "Deletes a user") @Operation(summary = "Deletes a user", responses = {
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), @ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") }) @ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeUserApi(@Context UriInfo uri, public Response removeUserApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username, @PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @ApiParam(value = "User to remove") String id) { @PathParam("id") @Parameter(description = "User to remove") String id) {
if (!authorizeUser(username)) { if (!authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
} }

View File

@ -41,6 +41,7 @@ import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -55,8 +56,6 @@ import javax.ws.rs.core.Response;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.openhab.io.hueemulation.internal.ConfigStore; import org.openhab.io.hueemulation.internal.ConfigStore;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
@ -82,8 +81,7 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault @NonNullByDefault
@Component(immediate = false, // Don't start the upnp server on its own. Must be pulled in by HueEmulationService. @Component(immediate = false, // Don't start the upnp server on its own. Must be pulled in by HueEmulationService.
configurationPolicy = ConfigurationPolicy.IGNORE, property = { configurationPolicy = ConfigurationPolicy.IGNORE, property = {
EventConstants.EVENT_TOPIC + "=" + ConfigStore.EVENT_ADDRESS_CHANGED, EventConstants.EVENT_TOPIC + "=" + ConfigStore.EVENT_ADDRESS_CHANGED }, //
"com.eclipsesource.jaxrs.publish=false" }, //
service = { UpnpServer.class, EventHandler.class }) service = { UpnpServer.class, EventHandler.class })
public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConfigWithRuntime>, EventHandler { public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConfigWithRuntime>, EventHandler {
/** /**
@ -121,6 +119,9 @@ public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConf
@Reference @Reference
protected @NonNullByDefault({}) HttpService httpService; protected @NonNullByDefault({}) HttpService httpService;
@Reference
protected @NonNullByDefault({}) ClientBuilder clientBuilder;
public boolean overwriteReadyToFalse = false; public boolean overwriteReadyToFalse = false;
private HueEmulationConfigWithRuntime config; private HueEmulationConfigWithRuntime config;
@ -242,11 +243,7 @@ public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConf
} }
selfTests.clear(); selfTests.clear();
Client client = clientBuilder.connectTimeout(1, TimeUnit.SECONDS).readTimeout(1, TimeUnit.SECONDS).build();
ClientConfig configuration = new ClientConfig();
configuration = configuration.property(ClientProperties.CONNECT_TIMEOUT, 1000);
configuration = configuration.property(ClientProperties.READ_TIMEOUT, 1000);
Client client = ClientBuilder.newClient(configuration);
Response response; Response response;
String url = ""; String url = "";
try { try {

View File

@ -34,12 +34,9 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.logging.LoggingFeature; import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.logging.LoggingFeature.Verbosity; import org.glassfish.jersey.logging.LoggingFeature.Verbosity;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.openhab.core.events.EventPublisher; import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.MetadataRegistry; import org.openhab.core.items.MetadataRegistry;
import org.openhab.core.net.NetworkAddressService; import org.openhab.core.net.NetworkAddressService;
@ -61,15 +58,19 @@ import org.osgi.service.cm.ConfigurationAdmin;
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.WARN)
public class CommonSetup { public class CommonSetup {
public UserManagement userManagement; public String basePath;
public Client client;
public @Mock EventPublisher eventPublisher;
public ConfigStore cs; public ConfigStore cs;
public HttpServer server;
UserManagement userManagement;
AutoCloseable mocksCloseable;
@Mock
EventPublisher eventPublisher;
@Mock @Mock
ConfigurationAdmin configAdmin; ConfigurationAdmin configAdmin;
@ -101,11 +102,9 @@ public class CommonSetup {
} }
}; };
public Client client;
public HttpServer server;
public String basePath;
public CommonSetup(boolean withMetadata) throws IOException { public CommonSetup(boolean withMetadata) throws IOException {
mocksCloseable = MockitoAnnotations.openMocks(this);
when(configAdmin.getConfiguration(anyString())).thenReturn(configAdminConfig); when(configAdmin.getConfiguration(anyString())).thenReturn(configAdminConfig);
when(configAdmin.getConfiguration(anyString(), any())).thenReturn(configAdminConfig); when(configAdmin.getConfiguration(anyString(), any())).thenReturn(configAdminConfig);
Dictionary<String, Object> mockProperties = new Hashtable<>(); Dictionary<String, Object> mockProperties = new Hashtable<>();
@ -154,12 +153,14 @@ public class CommonSetup {
client = ClientBuilder.newClient(); client = ClientBuilder.newClient();
} }
public void dispose() { public void dispose() throws Exception {
if (client != null) { if (client != null) {
client.close(); client.close();
} }
if (server != null) { if (server != null) {
server.shutdownNow(); server.shutdownNow();
} }
mocksCloseable.close();
} }
} }

View File

@ -62,7 +62,7 @@ public class ItemUIDtoHueIDMappingTests {
} }
@AfterEach @AfterEach
public void tearDown() { public void tearDown() throws Exception {
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -83,7 +83,7 @@ public class LightsAndGroupsTests {
} }
@AfterEach @AfterEach
public void tearDown() { public void tearDown() throws Exception {
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -61,6 +61,7 @@ import com.google.gson.reflect.TypeToken;
*/ */
@NonNullByDefault @NonNullByDefault
public class RulesTests { public class RulesTests {
protected @NonNullByDefault({}) CommonSetup commonSetup; protected @NonNullByDefault({}) CommonSetup commonSetup;
protected @NonNullByDefault({}) ConfigStore cs; protected @NonNullByDefault({}) ConfigStore cs;
protected @NonNullByDefault({}) ItemRegistry itemRegistry; protected @NonNullByDefault({}) ItemRegistry itemRegistry;
@ -106,7 +107,7 @@ public class RulesTests {
} }
@AfterEach @AfterEach
public void tearDown() { public void tearDown() throws Exception {
RuleUtils.random = new Random(); RuleUtils.random = new Random();
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -99,7 +99,7 @@ public class SceneTests {
} }
@AfterEach @AfterEach
public void tearDown() { public void tearDown() throws Exception {
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -99,7 +99,7 @@ public class ScheduleTests {
} }
@AfterEach @AfterEach
public void tearDown() { public void tearDown() throws Exception {
RuleUtils.random = new Random(); RuleUtils.random = new Random();
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -86,7 +86,7 @@ public class SensorTests {
} }
@AfterEach @AfterEach
public void tearDown() { public void tearDown() throws Exception {
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -62,7 +62,7 @@ public class UsersAndConfigTests {
} }
@AfterEach @AfterEach
public void tearDown() { public void tearDown() throws Exception {
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -14,7 +14,7 @@ package org.openhab.io.hueemulation.internal.rest.mocks;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.items.Item; import org.openhab.core.items.Item;
import org.openhab.core.net.NetworkAddressService; import org.openhab.core.net.NetworkAddressService;
@ -27,6 +27,7 @@ import org.osgi.service.cm.ConfigurationAdmin;
* *
* @author David Graeff - Initial contribution * @author David Graeff - Initial contribution
*/ */
@NonNullByDefault
public class ConfigStoreWithoutMetadata extends ConfigStore { public class ConfigStoreWithoutMetadata extends ConfigStore {
public ConfigStoreWithoutMetadata(NetworkAddressService networkAddressService, ConfigurationAdmin configAdmin, public ConfigStoreWithoutMetadata(NetworkAddressService networkAddressService, ConfigurationAdmin configAdmin,
@ -39,7 +40,7 @@ public class ConfigStoreWithoutMetadata extends ConfigStore {
} }
@Override @Override
public @NonNull String mapItemUIDtoHueID(@Nullable Item item) { public String mapItemUIDtoHueID(@Nullable Item item) {
if (item == null) { if (item == null) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }

View File

@ -44,7 +44,6 @@ public class DummyRuleRegistry implements RuleRegistry {
return items.values(); return items.values();
} }
@NonNullByDefault({})
@Override @Override
public Stream<Rule> stream() { public Stream<Rule> stream() {
return items.values().stream(); return items.values().stream();
@ -87,13 +86,11 @@ public class DummyRuleRegistry implements RuleRegistry {
return put; return put;
} }
@NonNullByDefault({})
@Override @Override
public Collection<Rule> getByTag(String tag) { public Collection<Rule> getByTag(@Nullable String tag) {
return Collections.emptyList(); return Collections.emptyList();
} }
@NonNullByDefault({})
@Override @Override
public Collection<Rule> getByTags(String... tags) { public Collection<Rule> getByTags(String... tags) {
return Collections.emptyList(); return Collections.emptyList();

View File

@ -25,6 +25,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.glassfish.grizzly.osgi.httpservice.HttpServiceImpl; import org.glassfish.grizzly.osgi.httpservice.HttpServiceImpl;
@ -81,6 +82,7 @@ public class UpnpTests {
return null; return null;
}).when(executor).execute(ArgumentMatchers.any()); }).when(executor).execute(ArgumentMatchers.any());
subject = new UpnpServer(executor); subject = new UpnpServer(executor);
subject.clientBuilder = ClientBuilder.newBuilder();
subject.httpService = httpServiceImpl; subject.httpService = httpServiceImpl;
subject.cs = commonSetup.cs; subject.cs = commonSetup.cs;
subject.overwriteReadyToFalse = true; subject.overwriteReadyToFalse = true;
@ -94,7 +96,7 @@ public class UpnpTests {
} }
@AfterAll @AfterAll
public static void tearDownHttp() { public static void tearDownHttp() throws Exception {
mainHttpHandler.unregisterAll(); mainHttpHandler.unregisterAll();
commonSetup.dispose(); commonSetup.dispose();
} }

View File

@ -19,11 +19,12 @@
<modules> <modules>
<!-- io --> <!-- io -->
<module>org.openhab.io.homekit</module> <module>org.openhab.io.homekit</module>
<module>org.openhab.io.hueemulation</module>
<module>org.openhab.io.imperihome</module> <module>org.openhab.io.imperihome</module>
<module>org.openhab.io.mqttembeddedbroker</module>
<module>org.openhab.io.neeo</module> <module>org.openhab.io.neeo</module>
<module>org.openhab.io.openhabcloud</module> <module>org.openhab.io.openhabcloud</module>
<module>org.openhab.io.transport.modbus</module> <module>org.openhab.io.transport.modbus</module>
<module>org.openhab.io.mqttembeddedbroker</module>
<!-- transformations --> <!-- transformations -->
<module>org.openhab.transform.bin2json</module> <module>org.openhab.transform.bin2json</module>
<module>org.openhab.transform.exec</module> <module>org.openhab.transform.exec</module>

View File

@ -52,7 +52,6 @@
<!-- temporarily exclude add-ons, which are still excluded from the build --> <!-- temporarily exclude add-ons, which are still excluded from the build -->
<exclude name="**/org.openhab.binding.netatmo/**/feature.xml"/> <exclude name="**/org.openhab.binding.netatmo/**/feature.xml"/>
<exclude name="**/org.openhab.io.hueemulation/**/feature.xml"/>
</fileset> </fileset>
<filterchain> <filterchain>
<linecontainsRegExp> <linecontainsRegExp>