Migrate to JAX-RS Whiteboard (#242)
* Update REST Docs feature dependency * Annotate habot for whiteboard * Fix default UI classpath, fix null context ID warning * Annotate cometvisu for whiteboard Signed-off-by: Wouter Born <github@maindrain.net>pull/249/head
parent
e624727564
commit
aa05fab9d2
|
@ -1,53 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.ui.bundles</groupId>
|
||||
<artifactId>org.openhab.ui.reactor.bundles</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.ui.cometvisu.php</artifactId>
|
||||
|
||||
<name>openHAB UI :: Bundles :: CometVisu :: PHP Support</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openhab</groupId>
|
||||
<artifactId>com.caucho.quercus</artifactId>
|
||||
<version>4.0.45</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.ui.bundles</groupId>
|
||||
<artifactId>org.openhab.ui.cometvisu</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.ui.bundles</groupId>
|
||||
<artifactId>org.openhab.ui.dashboard</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.model.sitemap</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.ui</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.ui.icon</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -28,19 +28,16 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="ignore_optional_problems" value="true"/>
|
||||
<attribute name="m2e-apt" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="src/gen/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/classes" path="src-gen">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
Bundle-SymbolicName: ${project.artifactId}
|
||||
Bundle-Activator: org.openhab.ui.cometvisu.internal.CvActivator
|
|
@ -68,11 +68,6 @@
|
|||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.9.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.media</groupId>
|
||||
<artifactId>jersey-media-multipart</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.ui.bundles</groupId>
|
||||
<artifactId>org.openhab.ui.reactor.bundles</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.ui.cometvisu</artifactId>
|
||||
|
||||
<name>openHAB UI :: Bundles :: CometVisu :: Backend</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-core</artifactId>
|
||||
<version>2.2.11</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>2.2.11</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.2.12</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.ui.bundles</groupId>
|
||||
<artifactId>org.openhab.ui.dashboard</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.model.sitemap</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.ui</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.ui.icon</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.rrd4j</groupId>
|
||||
<artifactId>rrd4j</artifactId>
|
||||
<version>3.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<generatedSourcesDirectory>src/gen/java</generatedSourcesDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>jaxb2-maven-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<configuration>
|
||||
<!-- The package of your generated sources -->
|
||||
<packageName>org.openhab.ui.cometvisu.internal.config.beans</packageName>
|
||||
<sources>
|
||||
<source>src/main/resources/visu_config.xsd</source>
|
||||
</sources>
|
||||
<outputDirectory>src/gen/java</outputDirectory>
|
||||
<locale>en</locale>
|
||||
<clearOutputDir>false</clearOutputDir>
|
||||
<generateEpisode>false</generateEpisode>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -1,97 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.ui.cometvisu.internal;
|
||||
|
||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||
import org.glassfish.jersey.media.sse.SseFeature;
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Bundle activator for CometVisu backend.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial Contribution and API
|
||||
*
|
||||
*/
|
||||
public class CvActivator implements BundleActivator {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CvActivator.class);
|
||||
|
||||
private static BundleContext context;
|
||||
|
||||
private ServiceRegistration<?> sseFeatureRegistration;
|
||||
|
||||
private ServiceRegistration<?> blockingAsyncFeatureRegistration;
|
||||
|
||||
private ServiceRegistration<?> multiPartRegistration;
|
||||
|
||||
/**
|
||||
* Called whenever the OSGi framework starts our bundle
|
||||
*/
|
||||
@Override
|
||||
public void start(BundleContext bc) throws Exception {
|
||||
context = bc;
|
||||
|
||||
String featureName = SseFeature.class.getName();
|
||||
if (bc.getServiceReference(featureName) == null) {
|
||||
sseFeatureRegistration = bc.registerService(featureName, new SseFeature(), null);
|
||||
|
||||
logger.debug("CometVisu - SseFeature registered.");
|
||||
}
|
||||
featureName = MultiPartFeature.class.getName();
|
||||
if (bc.getServiceReference(featureName) == null) {
|
||||
multiPartRegistration = bc.registerService(featureName, new MultiPartFeature(), null);
|
||||
|
||||
logger.debug("CometVisu - MultiPartFeature registered.");
|
||||
}
|
||||
|
||||
logger.debug("CometVisu has been started.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the OSGi framework stops our bundle
|
||||
*/
|
||||
@Override
|
||||
public void stop(BundleContext bc) throws Exception {
|
||||
context = null;
|
||||
|
||||
if (sseFeatureRegistration != null) {
|
||||
sseFeatureRegistration.unregister();
|
||||
logger.debug("CometVisu - SseFeature unregistered.");
|
||||
}
|
||||
|
||||
if (blockingAsyncFeatureRegistration != null) {
|
||||
blockingAsyncFeatureRegistration.unregister();
|
||||
logger.debug("CometVisu - BlockingAsyncFeature unregistered.");
|
||||
}
|
||||
|
||||
if (multiPartRegistration != null) {
|
||||
multiPartRegistration.unregister();
|
||||
logger.debug("CometVisu - MultiPartFeature unregistered.");
|
||||
}
|
||||
|
||||
logger.debug("CometVisu has been stopped.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle context of this bundle
|
||||
*
|
||||
* @return the bundle context
|
||||
*/
|
||||
public static BundleContext getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.ui.cometvisu.internal.async;
|
||||
|
||||
import org.glassfish.hk2.utilities.binding.AbstractBinder;
|
||||
import org.glassfish.jersey.internal.inject.CustomAnnotationLiteral;
|
||||
import org.glassfish.jersey.servlet.spi.AsyncContextDelegateProvider;
|
||||
|
||||
/**
|
||||
* An {@link AbstractBinder} implementation that registers our custom
|
||||
* {@link BlockingAsyncContextDelegateProvider} class as an implementation of
|
||||
* the {@link AsyncContextDelegateProvider} SPI interface.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial Contribution and API
|
||||
*
|
||||
*/
|
||||
public class BlockingAsyncBinder extends AbstractBinder {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
// the qualifiedBy is needed in order for our implementation to be used
|
||||
// if there are multiple implementations of AsyncContextDelegateProvider
|
||||
bind(new BlockingAsyncContextDelegateProvider()).to(AsyncContextDelegateProvider.class)
|
||||
.qualifiedBy(CustomAnnotationLiteral.INSTANCE);
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.ui.cometvisu.internal.async;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.glassfish.jersey.media.sse.SseFeature;
|
||||
import org.glassfish.jersey.servlet.spi.AsyncContextDelegate;
|
||||
import org.glassfish.jersey.servlet.spi.AsyncContextDelegateProvider;
|
||||
import org.openhab.ui.cometvisu.internal.util.SseUtil;
|
||||
|
||||
/**
|
||||
* An {@link AsyncContextDelegateProvider} implementation that returns a
|
||||
* blocking {@link AsyncContextDelegate}, which blocks while the connection is
|
||||
* alive if the response content-type is {@link SseFeature #SERVER_SENT_EVENTS}
|
||||
* or throws an UnsupportedOperationException otherwise. The blocking continues
|
||||
* until the response can longer be written to.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial Contribution and API
|
||||
*
|
||||
*/
|
||||
public class BlockingAsyncContextDelegateProvider implements AsyncContextDelegateProvider {
|
||||
|
||||
@Override
|
||||
public final AsyncContextDelegate createDelegate(final HttpServletRequest request,
|
||||
final HttpServletResponse response) {
|
||||
return new BlockingAsyncContextDelegate(request, response);
|
||||
}
|
||||
|
||||
private static final class BlockingAsyncContextDelegate implements AsyncContextDelegate {
|
||||
private static final int PING_TIMEOUT = 15 * 1000;
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private volatile boolean isRunning;
|
||||
|
||||
private BlockingAsyncContextDelegate(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete() {
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suspend() throws IllegalStateException {
|
||||
if (SseUtil.shouldAsyncBlock()) {
|
||||
isRunning = true;
|
||||
|
||||
synchronized (this) {
|
||||
while (isRunning) {
|
||||
try {
|
||||
this.wait(PING_TIMEOUT);
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
|
||||
// write a new line to the OutputStream and flush to
|
||||
// check connectivity. If the other peer closes the
|
||||
// connection, the first flush() should generate a
|
||||
// TCP reset that is detected on the second flush()
|
||||
outputStream.write('\n');
|
||||
response.flushBuffer();
|
||||
|
||||
outputStream.write('\n');
|
||||
response.flushBuffer();
|
||||
} catch (Exception exception) {
|
||||
// If an exception has occurred during write and
|
||||
// flush we consider the connection closed, attempt
|
||||
// to close the output stream and stop blocking.
|
||||
try {
|
||||
response.getOutputStream().close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException("ASYNCHRONOUS PROCESSING IS NOT SUPPORTED!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.ui.cometvisu.internal.async;
|
||||
|
||||
import javax.ws.rs.core.Feature;
|
||||
import javax.ws.rs.core.FeatureContext;
|
||||
|
||||
/**
|
||||
* A {@link Feature} implementation that registers our custom
|
||||
* {@link BlockingAsyncBinder}.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial Contribution and API
|
||||
*
|
||||
*/
|
||||
public class BlockingAsyncFeature implements Feature {
|
||||
|
||||
@Override
|
||||
public boolean configure(FeatureContext context) {
|
||||
if (context.getConfiguration().isEnabled(BlockingAsyncFeature.class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
context.register(new BlockingAsyncBinder());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.core.items.GroupItem;
|
||||
import org.openhab.core.items.Item;
|
||||
|
@ -55,6 +56,11 @@ 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.ReferencePolicy;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.rrd4j.ConsolFun;
|
||||
import org.rrd4j.core.FetchData;
|
||||
import org.rrd4j.core.FetchRequest;
|
||||
|
@ -73,9 +79,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* used by the diagram plugin
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
*
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_CHART_ALIAS)
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_CHART_ALIAS)
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_CHART_ALIAS)
|
||||
public class ChartResource implements RESTResource {
|
||||
|
|
|
@ -21,12 +21,18 @@ import javax.ws.rs.Path;
|
|||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.ui.cometvisu.internal.Config;
|
||||
import org.openhab.ui.cometvisu.internal.ManagerSettings;
|
||||
import org.openhab.ui.cometvisu.internal.backend.model.rest.CheckResponse;
|
||||
import org.openhab.ui.cometvisu.internal.backend.model.rest.EnvironmentState;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
@ -37,9 +43,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* Check filesystem backend for the cometvisu manager.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
*
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/fs/check")
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/fs/check")
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/fs/check")
|
||||
public class CheckResource implements RESTResource {
|
||||
|
|
|
@ -19,13 +19,20 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.ui.cometvisu.internal.Config;
|
||||
import org.openhab.ui.cometvisu.internal.util.ClientInstaller;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
|
@ -35,9 +42,15 @@ import io.swagger.annotations.ApiResponses;
|
|||
* Allows certain actions to configure the CometVisu backend through the REST api.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_CONFIG_ALIAS)
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_CONFIG_ALIAS)
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_CONFIG_ALIAS)
|
||||
public class ConfigResource implements RESTResource {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ConfigResource.class);
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.ws.rs.QueryParam;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.core.items.GroupItem;
|
||||
import org.openhab.core.items.Item;
|
||||
|
@ -43,6 +44,11 @@ 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.ReferencePolicy;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -55,9 +61,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* DataProvider backend for the cometvisu manager.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
*
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/data")
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/data")
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/data")
|
||||
public class DataProviderResource implements RESTResource {
|
||||
|
|
|
@ -14,6 +14,8 @@ package org.openhab.ui.cometvisu.internal.backend.rest;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
|
@ -22,6 +24,7 @@ import org.openhab.core.types.State;
|
|||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface EventBroadcaster {
|
||||
/**
|
||||
* Broadcasts an event described by the given parameters to all currently
|
||||
|
@ -65,5 +68,5 @@ public interface EventBroadcaster {
|
|||
* - the item that is listened to
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Class<? extends State>> getClientItems(Item item);
|
||||
public Map<String, @Nullable Class<? extends State>> getClientItems(Item item);
|
||||
}
|
||||
|
|
|
@ -13,27 +13,29 @@
|
|||
package org.openhab.ui.cometvisu.internal.backend.rest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.ui.cometvisu.internal.Config;
|
||||
import org.openhab.ui.cometvisu.internal.ManagerSettings;
|
||||
|
@ -42,6 +44,11 @@ import org.openhab.ui.cometvisu.internal.util.FileOperationException;
|
|||
import org.openhab.ui.cometvisu.internal.util.FsUtil;
|
||||
import org.openhab.ui.cometvisu.internal.util.MountedFile;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -55,9 +62,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* Filesystem backend for the cometvisu manager.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
*
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/fs")
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/fs")
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/fs")
|
||||
public class FsResource implements RESTResource {
|
||||
|
@ -103,21 +114,22 @@ public class FsResource implements RESTResource {
|
|||
@ApiOperation(value = "Create a binary file")
|
||||
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), @ApiResponse(code = 403, message = "not allowed"),
|
||||
@ApiResponse(code = 406, message = "File already exists") })
|
||||
public Response createBinary(
|
||||
public Response createBinary(@Context HttpServletRequest request,
|
||||
@ApiParam(value = "Relative path inside the config folder", required = true) @QueryParam("path") String path,
|
||||
@QueryParam("type") String type,
|
||||
@ApiParam(value = "CRC32 hash of the file content") @QueryParam("hash") String hash,
|
||||
@ApiParam(value = "force overriding existing file") @DefaultValue("false") @FormDataParam("force") Boolean force,
|
||||
@ApiParam(value = "file content") @FormDataParam("file") InputStream fileInputStream,
|
||||
@FormDataParam("file") FormDataContentDisposition fileMetaData) {
|
||||
@ApiParam(value = "force overriding existing file") @DefaultValue("false") @FormParam("force") Boolean force,
|
||||
@ApiParam(value = "file content") @FormParam("file") Object fileParam) {
|
||||
MountedFile target;
|
||||
try {
|
||||
target = new MountedFile(Paths.get(path, fileMetaData.getFileName()).toString());
|
||||
MultipartRequestMap map = new MultipartRequestMap(request);
|
||||
File file = map.getFileParameter("file");
|
||||
target = new MountedFile(Paths.get(path, file.getName()).toString());
|
||||
File folder = target.toFile().toPath().getParent().toFile();
|
||||
if (!target.exists() || force) {
|
||||
if (!target.isReadonlyMount() && folder.canWrite()) {
|
||||
try {
|
||||
FsUtil.getInstance().saveFile(target.toFile(), fileInputStream, hash);
|
||||
FsUtil.getInstance().saveFile(target.toFile(), new FileInputStream(file), hash);
|
||||
return Response.ok().build();
|
||||
} catch (FileOperationException e) {
|
||||
return FsUtil.createErrorResponse(e);
|
||||
|
|
|
@ -30,6 +30,7 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.ui.cometvisu.internal.Config;
|
||||
import org.openhab.ui.cometvisu.internal.ManagerSettings;
|
||||
|
@ -37,6 +38,11 @@ import org.openhab.ui.cometvisu.internal.backend.model.rest.ConfigSection;
|
|||
import org.openhab.ui.cometvisu.internal.backend.model.rest.HiddenConfig;
|
||||
import org.openhab.ui.cometvisu.internal.util.FsUtil;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -53,9 +59,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* Hidden configuration backend for the cometvisu manager.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
*
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/config")
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/config")
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/config")
|
||||
public class HiddenConfigResource implements RESTResource {
|
||||
|
|
|
@ -22,12 +22,18 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.ui.cometvisu.internal.Config;
|
||||
import org.openhab.ui.cometvisu.internal.backend.model.ConfigBean;
|
||||
import org.openhab.ui.cometvisu.internal.backend.model.LoginBean;
|
||||
import org.openhab.ui.cometvisu.internal.backend.model.ResourcesBean;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
@ -39,8 +45,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* currently this is just a placeholder and does no real authentification
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_LOGIN_ALIAS)
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_LOGIN_ALIAS)
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_LOGIN_ALIAS)
|
||||
public class LoginResource implements RESTResource {
|
||||
|
|
|
@ -23,12 +23,18 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.ui.cometvisu.internal.Config;
|
||||
import org.openhab.ui.cometvisu.internal.util.FileOperationException;
|
||||
import org.openhab.ui.cometvisu.internal.util.FsUtil;
|
||||
import org.openhab.ui.cometvisu.internal.util.MountedFile;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -42,9 +48,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* Move/renames files for the CometVisu manager.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
*
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/fs/move")
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/fs/move")
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/fs/move")
|
||||
public class MoveResource implements RESTResource {
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.ui.cometvisu.internal.backend.rest;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
/**
|
||||
* Helper object for getting files from multi-part {@link HttpServletRequest}s.
|
||||
*
|
||||
* @author Wouter Born - Initial contribution
|
||||
*/
|
||||
public class MultipartRequestMap extends HashMap<String, List<Object>> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String DEFAULT_ENCODING = "UTF-8";
|
||||
|
||||
private String encoding;
|
||||
private String tempLocation;
|
||||
|
||||
public MultipartRequestMap(HttpServletRequest request) {
|
||||
this(request, System.getProperty("java.io.tmpdir"));
|
||||
}
|
||||
|
||||
public MultipartRequestMap(HttpServletRequest request, String tempLocation) {
|
||||
super();
|
||||
try {
|
||||
this.tempLocation = tempLocation;
|
||||
|
||||
encoding = request.getCharacterEncoding();
|
||||
if (encoding == null) {
|
||||
try {
|
||||
encoding = DEFAULT_ENCODING;
|
||||
request.setCharacterEncoding(DEFAULT_ENCODING);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Unsupported encoding: " + encoding, e);
|
||||
}
|
||||
}
|
||||
|
||||
for (Part part : request.getParts()) {
|
||||
String fileName = part.getSubmittedFileName();
|
||||
if (fileName == null) {
|
||||
putMulti(part.getName(), getValue(part));
|
||||
} else {
|
||||
processFilePart(part, fileName);
|
||||
}
|
||||
}
|
||||
} catch (IOException | ServletException e) {
|
||||
throw new IllegalStateException("Failed to get request parts", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringParameter(String name) {
|
||||
List<Object> list = get(name);
|
||||
return (list != null) ? (String) get(name).get(0) : null;
|
||||
}
|
||||
|
||||
public File getFileParameter(String name) {
|
||||
List<Object> list = get(name);
|
||||
return (list != null) ? (File) get(name).get(0) : null;
|
||||
}
|
||||
|
||||
private void processFilePart(Part part, String fileName) throws IOException {
|
||||
File tempFile = new File(tempLocation, fileName);
|
||||
tempFile.createNewFile();
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
try (BufferedInputStream input = new BufferedInputStream(part.getInputStream(), 8192);
|
||||
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(tempFile), 8192);) {
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
for (int length = 0; ((length = input.read(buffer)) > 0);) {
|
||||
output.write(buffer, 0, length);
|
||||
}
|
||||
}
|
||||
part.delete();
|
||||
putMulti(part.getName(), tempFile);
|
||||
}
|
||||
|
||||
private String getValue(Part part) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), encoding));
|
||||
StringBuilder value = new StringBuilder();
|
||||
char[] buffer = new char[8192];
|
||||
for (int length; (length = reader.read(buffer)) > 0;) {
|
||||
value.append(buffer, 0, length);
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private <T> void putMulti(final String key, final T value) {
|
||||
List<Object> values = super.get(key);
|
||||
|
||||
if (values == null) {
|
||||
values = new ArrayList<>();
|
||||
values.add(value);
|
||||
put(key, values);
|
||||
} else {
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,12 +29,16 @@ import javax.ws.rs.Path;
|
|||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import javax.ws.rs.sse.Sse;
|
||||
import javax.ws.rs.sse.SseEventSink;
|
||||
|
||||
import org.glassfish.jersey.media.sse.EventOutput;
|
||||
import org.glassfish.jersey.media.sse.SseBroadcaster;
|
||||
import org.glassfish.jersey.media.sse.SseFeature;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.core.io.rest.SseBroadcaster;
|
||||
import org.openhab.core.items.GenericItem;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemFactory;
|
||||
|
@ -45,8 +49,15 @@ import org.openhab.ui.cometvisu.internal.Config;
|
|||
import org.openhab.ui.cometvisu.internal.backend.model.StateBean;
|
||||
import org.openhab.ui.cometvisu.internal.listeners.StateEventListener;
|
||||
import org.openhab.ui.cometvisu.internal.util.SseUtil;
|
||||
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.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -60,48 +71,56 @@ import io.swagger.annotations.ApiResponses;
|
|||
* SSE communication
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component(immediate = true)
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_READ_ALIAS)
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_READ_ALIAS)
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_READ_ALIAS)
|
||||
@NonNullByDefault
|
||||
public class ReadResource implements EventBroadcaster, RESTResource {
|
||||
private final Logger logger = LoggerFactory.getLogger(ReadResource.class);
|
||||
|
||||
private SseBroadcaster broadcaster = new SseBroadcaster();
|
||||
private SseBroadcaster<SseSinkInfo> broadcaster = new SseBroadcaster<>();
|
||||
|
||||
private final ExecutorService executorService;
|
||||
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
private ItemRegistry itemRegistry;
|
||||
private final ItemRegistry itemRegistry;
|
||||
|
||||
private StateEventListener stateEventListener;
|
||||
private final StateEventListener stateEventListener = new StateEventListener(this);
|
||||
|
||||
private List<String> itemNames = new ArrayList<>();
|
||||
private Map<Item, Map<String, Class<? extends State>>> items = new HashMap<>();
|
||||
private Map<Item, Map<String, @Nullable Class<? extends State>>> items = new HashMap<>();
|
||||
|
||||
@Context
|
||||
private UriInfo uriInfo;
|
||||
private @NonNullByDefault({}) UriInfo uriInfo;
|
||||
|
||||
@Context
|
||||
private HttpServletResponse response;
|
||||
private @NonNullByDefault({}) HttpServletResponse response;
|
||||
|
||||
@Context
|
||||
private HttpServletRequest request;
|
||||
private @NonNullByDefault({}) HttpServletRequest request;
|
||||
|
||||
private @NonNullByDefault({}) Sse sse;
|
||||
|
||||
private Collection<ItemFactory> itemFactories = new CopyOnWriteArrayList<>();
|
||||
|
||||
public ReadResource() {
|
||||
this.executorService = Executors.newSingleThreadExecutor();
|
||||
this.stateEventListener = new StateEventListener();
|
||||
this.stateEventListener.setEventBroadcaster(this);
|
||||
}
|
||||
|
||||
@Reference
|
||||
protected void setItemRegistry(ItemRegistry itemRegistry) {
|
||||
@Activate
|
||||
public ReadResource(@Reference ItemRegistry itemRegistry) {
|
||||
this.itemRegistry = itemRegistry;
|
||||
}
|
||||
|
||||
protected void unsetItemRegistry(ItemRegistry itemRegistry) {
|
||||
this.itemRegistry = null;
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
broadcaster.close();
|
||||
}
|
||||
|
||||
@Context
|
||||
public void setSse(final Sse sse) {
|
||||
this.sse = sse;
|
||||
}
|
||||
|
||||
protected void addItemFactory(ItemFactory itemFactory) {
|
||||
|
@ -123,16 +142,15 @@ public class ReadResource implements EventBroadcaster, RESTResource {
|
|||
* @throws InterruptedException
|
||||
*/
|
||||
@GET
|
||||
@Produces(SseFeature.SERVER_SENT_EVENTS)
|
||||
@Produces(MediaType.SERVER_SENT_EVENTS)
|
||||
@ApiOperation(value = "Creates the SSE stream for item states, sends all requested states once and then only changes states")
|
||||
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
|
||||
public Object getStates(@QueryParam("a") List<String> itemNames, @QueryParam("i") long index,
|
||||
@QueryParam("t") long time) throws IOException, InterruptedException {
|
||||
final EventOutput eventOutput = new EventOutput();
|
||||
public void getStates(@Context final SseEventSink sseEventSink, @QueryParam("a") List<String> itemNames,
|
||||
@QueryParam("i") long index, @QueryParam("t") long time) throws IOException, InterruptedException {
|
||||
|
||||
this.itemNames = itemNames;
|
||||
|
||||
broadcaster.add(eventOutput);
|
||||
broadcaster.add(sseEventSink, new SseSinkInfo(itemNames, index, time));
|
||||
|
||||
// get all requested items and send their states to the client
|
||||
items = new HashMap<>();
|
||||
|
@ -175,12 +193,10 @@ public class ReadResource implements EventBroadcaster, RESTResource {
|
|||
}
|
||||
}
|
||||
logger.debug("initially broadcasting {}/{} item states", states.size(), itemNames.size());
|
||||
broadcaster.broadcast(SseUtil.buildEvent(states));
|
||||
broadcaster.send(SseUtil.buildEvent(sse.newEventBuilder(), states));
|
||||
}
|
||||
// listen to state changes of the requested items
|
||||
registerItems();
|
||||
|
||||
return eventOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,11 +212,9 @@ public class ReadResource implements EventBroadcaster, RESTResource {
|
|||
}
|
||||
|
||||
/**
|
||||
* listens to state changes of the given item, if it is part of the
|
||||
* requested items
|
||||
* listens to state changes of the given item, if it is part of the requested items
|
||||
*
|
||||
* @param item
|
||||
* - the new item, that should be listened to
|
||||
* @param item the new item, that should be listened to
|
||||
*/
|
||||
@Override
|
||||
public void registerItem(Item item) {
|
||||
|
@ -216,8 +230,7 @@ public class ReadResource implements EventBroadcaster, RESTResource {
|
|||
* listens to state changes of the given item, if it is part of the
|
||||
* requested items
|
||||
*
|
||||
* @param item
|
||||
* - the new item, that should be listened to
|
||||
* @param item the new item, that should be listened to
|
||||
*/
|
||||
@Override
|
||||
public void unregisterItem(Item item) {
|
||||
|
@ -234,20 +247,23 @@ public class ReadResource implements EventBroadcaster, RESTResource {
|
|||
* Broadcasts an event described by the given parameters to all currently
|
||||
* listening clients.
|
||||
*
|
||||
* @param item
|
||||
* - the item which has changed
|
||||
* @param eventObject
|
||||
* - bean that can be converted to a JSON object.
|
||||
* @param item the item which has changed
|
||||
* @param eventObject bean that can be converted to a JSON object.
|
||||
*/
|
||||
@Override
|
||||
public void broadcastEvent(final Object eventObject) {
|
||||
if (sse == null) {
|
||||
logger.trace("broadcast skipped (no one listened since activation)");
|
||||
return;
|
||||
}
|
||||
|
||||
executorService.execute(() -> {
|
||||
broadcaster.broadcast(SseUtil.buildEvent(eventObject));
|
||||
broadcaster.send(SseUtil.buildEvent(sse.newEventBuilder(), eventObject));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<? extends State>> getClientItems(Item item) {
|
||||
public Map<String, @Nullable Class<? extends State>> getClientItems(Item item) {
|
||||
return items.get(item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.ui.cometvisu.internal.backend.rest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The specific information we need to hold for a SSE sink.
|
||||
*
|
||||
* @author Wouter Born - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SseSinkInfo {
|
||||
|
||||
public List<String> itemNames;
|
||||
public long index;
|
||||
public long time;
|
||||
|
||||
public SseSinkInfo(List<String> itemNames, long index, long time) {
|
||||
this.itemNames = itemNames;
|
||||
this.index = index;
|
||||
this.time = time;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import javax.ws.rs.core.Response.Status;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemNotFoundException;
|
||||
|
@ -35,6 +36,11 @@ import org.openhab.ui.cometvisu.internal.Config;
|
|||
import org.openhab.ui.cometvisu.internal.backend.model.SuccessBean;
|
||||
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.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -48,8 +54,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* handles state updates send by the CometVisu client and forwars them to the EventPublisher
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial contribution
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_WRITE_ALIAS)
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@Path(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_WRITE_ALIAS)
|
||||
@Api(Config.COMETVISU_BACKEND_ALIAS + "/" + Config.COMETVISU_BACKEND_WRITE_ALIAS)
|
||||
public class WriteResource implements RESTResource {
|
||||
|
|
|
@ -30,6 +30,10 @@ public class StateEventListener implements StateChangeListener {
|
|||
|
||||
private EventBroadcaster eventBroadcaster;
|
||||
|
||||
public StateEventListener(EventBroadcaster eventBroadcaster) {
|
||||
this.eventBroadcaster = eventBroadcaster;
|
||||
}
|
||||
|
||||
public void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
|
||||
this.eventBroadcaster = eventBroadcaster;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@ package org.openhab.ui.cometvisu.internal.util;
|
|||
import java.util.Date;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.sse.OutboundSseEvent;
|
||||
|
||||
import org.glassfish.jersey.media.sse.OutboundEvent;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.ui.cometvisu.internal.StateBeanMessageBodyWriter;
|
||||
import org.openhab.ui.cometvisu.internal.backend.model.StateBean;
|
||||
|
||||
|
@ -24,8 +25,9 @@ import org.openhab.ui.cometvisu.internal.backend.model.StateBean;
|
|||
* Utility class containing helper methods for the SSE implementation.
|
||||
*
|
||||
* @author Tobias Bräutigam - Initial Contribution and API
|
||||
*
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SseUtil {
|
||||
|
||||
/**
|
||||
|
@ -33,29 +35,23 @@ public class SseUtil {
|
|||
* {@link StateBean} created for the given eventType, objectIdentifier,
|
||||
* eventObject.
|
||||
*
|
||||
* @param eventType
|
||||
* - the event type for the event
|
||||
* @param objectIdentifier
|
||||
* - the identifier for the main event object
|
||||
* @param eventObject
|
||||
* - the eventObject to be included
|
||||
* @return a new OutboundEvent.
|
||||
* @param eventType the event type for the event
|
||||
* @param objectIdentifier the identifier for the main event object
|
||||
* @param eventObject the eventObject to be included
|
||||
* @return a new OutboundSseEvent
|
||||
*/
|
||||
public static OutboundEvent buildEvent(Object eventObject) {
|
||||
OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();
|
||||
public static OutboundSseEvent buildEvent(OutboundSseEvent.Builder eventBuilder, Object eventObject) {
|
||||
StateBeanMessageBodyWriter writer = new StateBeanMessageBodyWriter();
|
||||
Date date = new Date();
|
||||
OutboundEvent event = eventBuilder.mediaType(MediaType.APPLICATION_JSON_TYPE)
|
||||
.data(writer.serialize(eventObject)).id(String.valueOf(date.getTime())).build();
|
||||
|
||||
return event;
|
||||
return eventBuilder.mediaType(MediaType.APPLICATION_JSON_TYPE).data(writer.serialize(eventObject))
|
||||
.id(String.valueOf(date.getTime())).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to mark our current thread(request processing) that SSE blocking
|
||||
* should be enabled.
|
||||
*/
|
||||
private static ThreadLocal<Boolean> blockingSseEnabled = new ThreadLocal<Boolean>() {
|
||||
private static ThreadLocal<Boolean> blockingSseEnabled = new ThreadLocal<>() {
|
||||
@Override
|
||||
protected Boolean initialValue() {
|
||||
return false;
|
||||
|
|
|
@ -38,6 +38,7 @@ import javax.ws.rs.core.Response.Status;
|
|||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.openhab.core.auth.Role;
|
||||
import org.openhab.core.io.rest.LocaleService;
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.io.rest.RESTResource;
|
||||
import org.openhab.core.voice.VoiceManager;
|
||||
import org.openhab.core.voice.text.InterpretationException;
|
||||
|
@ -54,6 +55,11 @@ 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.ReferencePolicy;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -69,8 +75,13 @@ import io.swagger.annotations.ApiResponses;
|
|||
* This class describes the /habot resource of the REST API.
|
||||
*
|
||||
* @author Yannick Schaus - Initial contribution
|
||||
* @author Wouter Born - Migrated to JAX-RS Whiteboard Specification
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@JaxrsName(HABotResource.PATH_HABOT)
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@JSONRequired
|
||||
@RolesAllowed({ Role.USER, Role.ADMIN })
|
||||
@Path(HABotResource.PATH_HABOT)
|
||||
@Api(HABotResource.PATH_HABOT)
|
||||
|
|
|
@ -6,23 +6,6 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="web">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
|
@ -33,14 +16,15 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="target/generated-sources/annotations">
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="web/www">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
|
|
|
@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory;
|
|||
*
|
||||
* @author Yannick Schaus - Initial contribution
|
||||
*/
|
||||
@Component(immediate = true, name = "org.openhab.ui")
|
||||
@Component(immediate = true, name = "org.openhab.ui", property = { "httpContext.id:String=oh-ui-http-ctx" })
|
||||
public class UIService implements HttpContext {
|
||||
|
||||
private static final String APP_BASE = "app";
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
<feature>openhab-core-ui</feature>
|
||||
<feature dependency="true">openhab.tp-jaxb</feature>
|
||||
<feature dependency="true">openhab.tp-jackson</feature>
|
||||
<feature dependency="true">openhab.tp-kat.cpy-jersey-min-2.22.2</feature>
|
||||
<bundle dependency="true">mvn:org.rrd4j/rrd4j/3.4</bundle>
|
||||
<bundle>mvn:org.openhab.ui.bundles/org.openhab.ui.cometvisu/${project.version}</bundle>
|
||||
<config name="cometvisu">
|
||||
|
@ -89,7 +88,7 @@
|
|||
|
||||
<feature name="openhab-ui-restdocs" description="REST Documentation" version="${project.version}">
|
||||
<feature>openhab-core-ui</feature>
|
||||
<feature>openhab.tp-swagger-jax-rs-provider</feature>
|
||||
<feature>openhab-core-io-rest-swagger</feature>
|
||||
<bundle>mvn:org.openhab.ui.bundles/org.openhab.ui.restdocs/${project.version}</bundle>
|
||||
</feature>
|
||||
|
||||
|
|
Loading…
Reference in New Issue