[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>
<properties>
<bnd.importpackage>org.glassfish.jersey.*;resolution:="optional"</bnd.importpackage>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.glassfish.grizzly/grizzly-http-server -->
<dependency>

View File

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

View File

@ -14,9 +14,8 @@ package org.openhab.io.hueemulation.internal;
import java.util.Dictionary;
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.container.ContainerRequestContext;
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.Nullable;
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.LightsAndGroups;
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.upnp.UpnpServer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
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.EventConstants;
import org.osgi.service.event.EventHandler;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -75,17 +71,12 @@ import org.slf4j.LoggerFactory;
* @author David Graeff - Initial Contribution
*/
@NonNullByDefault
@Component(immediate = true, service = { HueEmulationService.class }, property = {
"com.eclipsesource.jaxrs.publish=false" })
@Component(immediate = true, service = HueEmulationService.class)
public class HueEmulationService implements EventHandler {
public static final String CONFIG_PID = "org.openhab.hueemulation";
public static final String RESTAPI_PATH = "/api";
@ApplicationPath(RESTAPI_PATH)
public static class JerseyApplication extends Application {
}
public static final String REST_APP_NAME = "HueEmulation";
@PreMatching
public class RequestInterceptor implements ContainerRequestFilter {
@ -117,6 +108,34 @@ public class HueEmulationService implements EventHandler {
}
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 LogAccessInterceptor accessInterceptor = new LogAccessInterceptor();
@ -144,9 +163,8 @@ public class HueEmulationService implements EventHandler {
@Reference
protected @NonNullByDefault({}) StatusResource statusResource;
@Reference
protected @NonNullByDefault({}) HttpService httpService;
private @NonNullByDefault({}) ServiceRegistration<?> eventHandler;
private @Nullable ServiceRegistration<?> eventHandler;
private @Nullable ServiceRegistration<Application> restService;
@Activate
protected void activate(BundleContext bc) {
@ -165,15 +183,11 @@ public class HueEmulationService implements EventHandler {
@Deactivate
protected void deactivate() {
try {
if (eventHandler != null) {
eventHandler.unregister();
}
} catch (IllegalStateException ignore) {
}
try {
httpService.unregister(RESTAPI_PATH);
} catch (IllegalArgumentException ignore) {
unregisterEventHandler();
ServiceRegistration<Application> localRestService = restService;
if (localRestService != null) {
localRestService.unregister();
}
}
@ -185,39 +199,26 @@ public class HueEmulationService implements EventHandler {
*/
@Override
public void handleEvent(@Nullable Event event) {
try { // Only receive this event once
eventHandler.unregister();
eventHandler = null;
} catch (IllegalStateException ignore) {
}
unregisterEventHandler();
ResourceConfig resourceConfig = ResourceConfig.forApplicationClass(JerseyApplication.class);
resourceConfig.property(ServerProperties.APPLICATION_NAME, "HueEmulation");
// don't look for implementations described by META-INF/services/*
resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
// disable auto discovery on server, as it's handled via OSGI
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();
ServiceRegistration<Application> localRestService = restService;
if (localRestService == null) {
RESTapplication app = new RESTapplication(RESTAPI_PATH);
BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
restService = context.registerService(Application.class, app, app.serviceProperties());
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
* before each serializion.
* before each serialization.
*/
@NonNullByDefault({})
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 {
// https://github.com/openhab/openhab-addons/issues/2881
// Apparently the maximum brightness is 254
public static int MAX_BRI = 254;
public static final int MAX_BRI = 254;
public int bri = 0;
/** white color temperature, 154 (cold) - 500 (warm) */
public static int MAX_CT = 500;
public static final int MAX_CT = 500;
public int ct = 500;
protected HueStateBulb() {

View File

@ -25,9 +25,9 @@ import org.openhab.core.library.types.PercentType;
*
*/
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 static int MAX_SAT = 254;
public static final int MAX_SAT = 254;
public int sat = 0;
// color as array of xy-coordinates

View File

@ -15,7 +15,7 @@ package org.openhab.io.hueemulation.internal.dto.changerequest;
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.
*
* @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.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.dto.HueUnauthorizedConfig;
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.component.annotations.Component;
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 io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
/**
* @author David Graeff - Initial contribution
*/
@Component(immediate = false, service = {
ConfigurationAccess.class }, property = "com.eclipsesource.jaxrs.publish=false")
@Component(immediate = false, service = ConfigurationAccess.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -62,8 +66,8 @@ public class ConfigurationAccess {
@GET
@Path("config")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the reduced configuration")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return the reduced configuration", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response getReducedConfigApi() {
return Response.ok(cs.gson.toJson(cs.ds.config, new TypeToken<HueUnauthorizedConfig>() {
}.getType())).build();
@ -72,10 +76,10 @@ public class ConfigurationAccess {
@GET
@Path("{username}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the full data store")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return the full data store", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response getAllApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -85,10 +89,10 @@ public class ConfigurationAccess {
@GET
@Path("{username}/config")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the configuration")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return the configuration", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response getFullConfigApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -98,10 +102,10 @@ public class ConfigurationAccess {
@PUT
@Path("{username}/config")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return the reduced configuration")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return the reduced configuration", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
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)) {
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.io.hueemulation.internal.ConfigStore;
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.StateUtils;
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.ReferenceCardinality;
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.LoggerFactory;
import com.google.gson.reflect.TypeToken;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
/**
* 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 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
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -241,10 +246,9 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET
@Path("{username}/lights")
@ApiOperation(value = "Return all lights")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return all lights", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getAllLightsApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -253,10 +257,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET
@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.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return new lights since last scan. Returns an empty list for openHAB as we do not cache that information.", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response getNewLights(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -265,10 +269,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@POST
@Path("{username}/lights")
@ApiOperation(value = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response postNewLights(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -277,11 +281,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET
@Path("{username}/lights/{id}")
@ApiOperation(value = "Return a light")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a light", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getLightApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "light id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -291,12 +294,12 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@DELETE
@Path("{username}/lights/{id}")
@ApiOperation(value = "Deletes the item that is represented by this id")
@ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"),
@ApiResponse(code = 403, message = "Access denied") })
@Operation(summary = "Deletes the item that is represented by this id", responses = {
@ApiResponse(responseCode = "200", description = "The item got removed"),
@ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeLightAPI(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -316,11 +319,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@PUT
@Path("{username}/lights/{id}")
@ApiOperation(value = "Rename a light")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Rename a light", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response renameLightApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "light id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -345,11 +347,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@PUT
@Path("{username}/lights/{id}/state")
@ApiOperation(value = "Set light state")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Set light state", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response setLightStateApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "light id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -391,11 +392,11 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@PUT
@Path("{username}/groups/{id}/action")
@ApiOperation(value = "Initiate group action")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Initiate group action", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response setGroupActionApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "group id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "group id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -436,10 +437,9 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET
@Path("{username}/groups")
@ApiOperation(value = "Return all groups")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return all groups", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getAllGroupsApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -448,11 +448,10 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@GET
@Path("{username}/groups/{id}")
@ApiOperation(value = "Return a group")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a group", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getGroupApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "group id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "group id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -462,10 +461,9 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@POST
@Path("{username}/groups")
@ApiOperation(value = "Create a new group")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Create a new group", responses = { @ApiResponse(responseCode = "200", description = "OK") })
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)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -506,12 +504,12 @@ public class LightsAndGroups implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@DELETE
@Path("{username}/groups/{id}")
@ApiOperation(value = "Deletes the item that is represented by this id")
@ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"),
@ApiResponse(code = 403, message = "Access denied") })
@Operation(summary = "Deletes the item that is represented by this id", responses = {
@ApiResponse(responseCode = "200", description = "The item got removed"),
@ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeGroupAPI(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "id") String id) {
if (!userManagement.authorizeUser(username)) {
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.ItemRegistry;
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.RuleUtils;
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.Deactivate;
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.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
/**
* Handles Hue rules via the automation subsystem and the corresponding REST interface
*
* @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
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -234,10 +239,9 @@ public class Rules implements RegistryChangeListener<Rule> {
@GET
@Path("{username}/rules")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return all rules")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return all rules", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getRulesApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -246,11 +250,10 @@ public class Rules implements RegistryChangeListener<Rule> {
@GET
@Path("{username}/rules/{id}")
@ApiOperation(value = "Return a rule")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a rule", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getRuleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "rule id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "rule id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -259,12 +262,12 @@ public class Rules implements RegistryChangeListener<Rule> {
@DELETE
@Path("{username}/rules/{id}")
@ApiOperation(value = "Deletes a rule")
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") })
@Operation(summary = "Deletes a rule", responses = {
@ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeRuleApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "Rule to remove") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "Rule to remove") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -279,11 +282,10 @@ public class Rules implements RegistryChangeListener<Rule> {
@PUT
@Path("{username}/rules/{id}")
@ApiOperation(value = "Set rule attributes")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Set rule attributes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response modifyRuleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "rule id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "rule id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -334,10 +336,9 @@ public class Rules implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "null" })
@POST
@Path("{username}/rules")
@ApiOperation(value = "Create a new rule")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Create a new rule", responses = { @ApiResponse(responseCode = "200", description = "OK") })
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)) {
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.State;
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.StateUtils;
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.Deactivate;
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.LoggerFactory;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
/**
* Handles Hue scenes via the automation subsystem and the corresponding REST interface
*
* @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
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -165,10 +170,9 @@ public class Scenes implements RegistryChangeListener<Rule> {
@GET
@Path("{username}/scenes")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return all scenes")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return all scenes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getScenesApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -178,11 +182,10 @@ public class Scenes implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "unused", "null" })
@GET
@Path("{username}/scenes/{id}")
@ApiOperation(value = "Return a scene")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a scene", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getSceneApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "scene id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "scene id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -208,12 +211,12 @@ public class Scenes implements RegistryChangeListener<Rule> {
@DELETE
@Path("{username}/scenes/{id}")
@ApiOperation(value = "Deletes a scene")
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") })
@Operation(summary = "Deletes a scene", responses = {
@ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeSceneApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "Scene to remove") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "Scene to remove") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -248,11 +251,10 @@ public class Scenes implements RegistryChangeListener<Rule> {
*/
@PUT
@Path("{username}/scenes/{id}")
@ApiOperation(value = "Set scene attributes")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Set scene attributes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response modifySceneApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "scene id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "scene id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -317,10 +319,9 @@ public class Scenes implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "null" })
@POST
@Path("{username}/scenes")
@ApiOperation(value = "Create a new scene")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Create a new scene", responses = { @ApiResponse(responseCode = "200", description = "OK") })
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)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -372,12 +373,11 @@ public class Scenes implements RegistryChangeListener<Rule> {
@PUT
@Path("{username}/scenes/{id}/lightstates/{lightid}")
@ApiOperation(value = "Set scene attributes")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Set scene attributes", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response modifySceneLightStateApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "scene id") String id,
@PathParam("lightid") @ApiParam(value = "light id") String lightid, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "scene id") String id,
@PathParam("lightid") @Parameter(description = "light id") String lightid, String body) {
if (!userManagement.authorizeUser(username)) {
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.config.core.Configuration;
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.RuleUtils;
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.Deactivate;
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.LoggerFactory;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
/**
* 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
*/
@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
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -228,10 +233,9 @@ public class Schedules implements RegistryChangeListener<Rule> {
@GET
@Path("{username}/schedules")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Return all schedules")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return all schedules", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getSchedulesApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -240,11 +244,10 @@ public class Schedules implements RegistryChangeListener<Rule> {
@GET
@Path("{username}/schedules/{id}")
@ApiOperation(value = "Return a schedule")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a schedule", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getScheduleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "schedule id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "schedule id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -253,12 +256,12 @@ public class Schedules implements RegistryChangeListener<Rule> {
@DELETE
@Path("{username}/schedules/{id}")
@ApiOperation(value = "Deletes a schedule")
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") })
@Operation(summary = "Deletes a schedule", responses = {
@ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeScheduleApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "Schedule to remove") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "Schedule to remove") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -273,11 +276,11 @@ public class Schedules implements RegistryChangeListener<Rule> {
@PUT
@Path("{username}/schedules/{id}")
@ApiOperation(value = "Set schedule attributes")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Set schedule attributes", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response modifyScheduleApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "schedule id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "schedule id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -311,10 +314,10 @@ public class Schedules implements RegistryChangeListener<Rule> {
@SuppressWarnings({ "null" })
@POST
@Path("{username}/schedules")
@ApiOperation(value = "Create a new schedule")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Create a new schedule", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
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)) {
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.library.CoreItemFactory;
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.dto.HueNewLights;
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.Deactivate;
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.LoggerFactory;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
/**
* 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
*/
@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
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -144,10 +149,9 @@ public class Sensors implements RegistryChangeListener<Item> {
@GET
@Path("{username}/sensors")
@ApiOperation(value = "Return all sensors")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return all sensors", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getAllSensorsApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -156,10 +160,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@GET
@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.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return new sensors since last scan. Returns an empty list for openHAB as we do not cache that information.", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response getNewSensors(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -168,10 +172,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@POST
@Path("{username}/sensors")
@ApiOperation(value = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response postNewLights(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -180,11 +184,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@GET
@Path("{username}/sensors/{id}")
@ApiOperation(value = "Return a sensor")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a sensor", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getSensorApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "sensor id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "sensor id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -194,11 +197,11 @@ public class Sensors implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@GET
@Path("{username}/sensors/{id}/config")
@ApiOperation(value = "Return a sensor config. Always empty")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a sensor config. Always empty", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
public Response getSensorConfigApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "sensor id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "sensor id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -214,12 +217,12 @@ public class Sensors implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@DELETE
@Path("{username}/sensors/{id}")
@ApiOperation(value = "Deletes the sensor that is represented by this id")
@ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"),
@ApiResponse(code = 403, message = "Access denied") })
@Operation(summary = "Deletes the sensor that is represented by this id", responses = {
@ApiResponse(responseCode = "200", description = "The item got removed"),
@ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeSensorAPI(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "id") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "id") String id) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -239,11 +242,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@SuppressWarnings({ "null", "unused" })
@PUT
@Path("{username}/sensors/{id}")
@ApiOperation(value = "Rename a sensor")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Rename a sensor", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response renameLightApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "light id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "light id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -267,11 +269,10 @@ public class Sensors implements RegistryChangeListener<Item> {
@PUT
@Path("{username}/sensors/{id}/state")
@ApiOperation(value = "Set sensor state")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Set sensor state", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response setSensorStateApi(@Context UriInfo uri, //
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "sensor id") String id, String body) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "sensor id") String id, String body) {
if (!userManagement.authorizeUser(username)) {
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.RegistryListener;
import org.openhab.io.hueemulation.internal.ConfigStore;
import org.openhab.io.hueemulation.internal.HueEmulationService;
import org.openhab.io.hueemulation.internal.upnp.UpnpServer;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
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.LoggerFactory;
@ -49,9 +53,11 @@ import org.slf4j.LoggerFactory;
*
* @author David Graeff - Initial contribution
*/
@Component(immediate = false, service = { StatusResource.class }, property = "com.eclipsesource.jaxrs.publish=false")
@Path("")
@Component(immediate = false, service = StatusResource.class)
@JaxrsResource
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + HueEmulationService.REST_APP_NAME + ")")
@NonNullByDefault
@Path("")
public class StatusResource implements RegistryListener {
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
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.storage.StorageService;
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.dto.HueUserAuth;
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.Component;
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.LoggerFactory;
import com.google.gson.reflect.TypeToken;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
/**
* 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
*/
@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
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -147,9 +152,9 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
}
@POST
@ApiOperation(value = "Create an API Key")
@ApiResponses(value = { @ApiResponse(code = 200, message = "API Key created"),
@ApiResponse(code = 403, message = "Link button not pressed") })
@Operation(summary = "Create an API Key", responses = {
@ApiResponse(responseCode = "200", description = "API Key created"),
@ApiResponse(responseCode = "403", description = "Link button not pressed") })
public Response createNewUser(@Context UriInfo uri, String body) {
if (!cs.ds.config.linkbutton) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.LINK_BUTTON_NOT_PRESSED,
@ -175,11 +180,10 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
@GET
@Path("{username}/config/whitelist/{userid}")
@ApiOperation(value = "Return a user")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return a user", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getUserApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("userid") @ApiParam(value = "User ID") String userid) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("userid") @Parameter(description = "User ID") String userid) {
if (!authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -188,10 +192,9 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
@GET
@Path("{username}/config/whitelist")
@ApiOperation(value = "Return all users")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
@Operation(summary = "Return all users", responses = { @ApiResponse(responseCode = "200", description = "OK") })
public Response getAllUsersApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username) {
@PathParam("username") @Parameter(description = "username") String username) {
if (!authorizeUser(username)) {
return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
}
@ -200,12 +203,12 @@ public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWi
@DELETE
@Path("{username}/config/whitelist/{id}")
@ApiOperation(value = "Deletes a user")
@ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"),
@ApiResponse(code = 403, message = "Access denied") })
@Operation(summary = "Deletes a user", responses = {
@ApiResponse(responseCode = "200", description = "The user got removed"),
@ApiResponse(responseCode = "403", description = "Access denied") })
public Response removeUserApi(@Context UriInfo uri,
@PathParam("username") @ApiParam(value = "username") String username,
@PathParam("id") @ApiParam(value = "User to remove") String id) {
@PathParam("username") @Parameter(description = "username") String username,
@PathParam("id") @Parameter(description = "User to remove") String id) {
if (!authorizeUser(username)) {
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.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
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.Nullable;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.openhab.io.hueemulation.internal.ConfigStore;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@ -82,8 +81,7 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault
@Component(immediate = false, // Don't start the upnp server on its own. Must be pulled in by HueEmulationService.
configurationPolicy = ConfigurationPolicy.IGNORE, property = {
EventConstants.EVENT_TOPIC + "=" + ConfigStore.EVENT_ADDRESS_CHANGED,
"com.eclipsesource.jaxrs.publish=false" }, //
EventConstants.EVENT_TOPIC + "=" + ConfigStore.EVENT_ADDRESS_CHANGED }, //
service = { UpnpServer.class, EventHandler.class })
public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConfigWithRuntime>, EventHandler {
/**
@ -121,6 +119,9 @@ public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConf
@Reference
protected @NonNullByDefault({}) HttpService httpService;
@Reference
protected @NonNullByDefault({}) ClientBuilder clientBuilder;
public boolean overwriteReadyToFalse = false;
private HueEmulationConfigWithRuntime config;
@ -242,11 +243,7 @@ public class UpnpServer extends HttpServlet implements Consumer<HueEmulationConf
}
selfTests.clear();
ClientConfig configuration = new ClientConfig();
configuration = configuration.property(ClientProperties.CONNECT_TIMEOUT, 1000);
configuration = configuration.property(ClientProperties.READ_TIMEOUT, 1000);
Client client = ClientBuilder.newClient(configuration);
Client client = clientBuilder.connectTimeout(1, TimeUnit.SECONDS).readTimeout(1, TimeUnit.SECONDS).build();
Response response;
String url = "";
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.Verbosity;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.mockito.MockitoAnnotations;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.MetadataRegistry;
import org.openhab.core.net.NetworkAddressService;
@ -61,15 +58,19 @@ import org.osgi.service.cm.ConfigurationAdmin;
*
* @author David Graeff - Initial contribution
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.WARN)
public class CommonSetup {
public UserManagement userManagement;
public @Mock EventPublisher eventPublisher;
public String basePath;
public Client client;
public ConfigStore cs;
public HttpServer server;
UserManagement userManagement;
AutoCloseable mocksCloseable;
@Mock
EventPublisher eventPublisher;
@Mock
ConfigurationAdmin configAdmin;
@ -101,11 +102,9 @@ public class CommonSetup {
}
};
public Client client;
public HttpServer server;
public String basePath;
public CommonSetup(boolean withMetadata) throws IOException {
mocksCloseable = MockitoAnnotations.openMocks(this);
when(configAdmin.getConfiguration(anyString())).thenReturn(configAdminConfig);
when(configAdmin.getConfiguration(anyString(), any())).thenReturn(configAdminConfig);
Dictionary<String, Object> mockProperties = new Hashtable<>();
@ -154,12 +153,14 @@ public class CommonSetup {
client = ClientBuilder.newClient();
}
public void dispose() {
public void dispose() throws Exception {
if (client != null) {
client.close();
}
if (server != null) {
server.shutdownNow();
}
mocksCloseable.close();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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