diff --git a/bundles/org.openhab.core.io.rest.optimize/.classpath b/bundles/org.openhab.core.io.rest.optimize/.classpath
new file mode 100644
index 0000000000..3c5e7d1755
--- /dev/null
+++ b/bundles/org.openhab.core.io.rest.optimize/.classpath
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.core.io.rest.optimize/.project b/bundles/org.openhab.core.io.rest.optimize/.project
new file mode 100644
index 0000000000..42e19ac066
--- /dev/null
+++ b/bundles/org.openhab.core.io.rest.optimize/.project
@@ -0,0 +1,23 @@
+
+
+ org.openhab.core.io.rest.optimize
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/bundles/org.openhab.core.io.rest.optimize/NOTICE b/bundles/org.openhab.core.io.rest.optimize/NOTICE
new file mode 100644
index 0000000000..6c17d0d8a4
--- /dev/null
+++ b/bundles/org.openhab.core.io.rest.optimize/NOTICE
@@ -0,0 +1,14 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-core
+
diff --git a/bundles/org.openhab.core.io.rest.optimize/bnd.bnd b/bundles/org.openhab.core.io.rest.optimize/bnd.bnd
new file mode 100644
index 0000000000..ab247a0d37
--- /dev/null
+++ b/bundles/org.openhab.core.io.rest.optimize/bnd.bnd
@@ -0,0 +1,2 @@
+Bundle-SymbolicName: ${project.artifactId}
+Bundle-Activator: org.eclipse.smarthome.io.rest.optimize.internal.Activator
\ No newline at end of file
diff --git a/bundles/org.openhab.core.io.rest.optimize/pom.xml b/bundles/org.openhab.core.io.rest.optimize/pom.xml
new file mode 100644
index 0000000000..392acc2386
--- /dev/null
+++ b/bundles/org.openhab.core.io.rest.optimize/pom.xml
@@ -0,0 +1,16 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.core.bundles
+ org.openhab.core.reactor.bundles
+ 2.5.0-SNAPSHOT
+
+
+ org.openhab.core.io.rest.optimize
+
+ openHAB Core :: Bundles :: REST JAX-RS Optimizations
+
+
diff --git a/bundles/org.openhab.core.io.rest.optimize/src/main/java/org/eclipse/smarthome/io/rest/optimize/internal/Activator.java b/bundles/org.openhab.core.io.rest.optimize/src/main/java/org/eclipse/smarthome/io/rest/optimize/internal/Activator.java
new file mode 100644
index 0000000000..3bf7324b1f
--- /dev/null
+++ b/bundles/org.openhab.core.io.rest.optimize/src/main/java/org/eclipse/smarthome/io/rest/optimize/internal/Activator.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2019 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.eclipse.smarthome.io.rest.optimize.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceRegistration;
+
+import com.eclipsesource.jaxrs.publisher.ResourceFilter;
+
+/**
+ * Activator
+ *
+ * It registers a {@link ResourceFilter} in order to prevent the JAX-RS implementation to
+ * enforce starting all services once they are registered.
+ *
+ * @author Simon Kaufmann - initial contribution and API.
+ *
+ */
+public class Activator implements BundleActivator {
+
+ private ServiceRegistration> resourceFilterRegistration;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ registerResourceFilter(context);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ unregisterResourceFilter();
+ }
+
+ private void registerResourceFilter(BundleContext context) throws InvalidSyntaxException {
+ resourceFilterRegistration = context.registerService(ResourceFilter.class.getName(), new ResourceFilterImpl(),
+ null);
+ }
+
+ private void unregisterResourceFilter() {
+ if (resourceFilterRegistration != null) {
+ resourceFilterRegistration.unregister();
+ resourceFilterRegistration = null;
+ }
+ }
+
+}
diff --git a/bundles/org.openhab.core.io.rest.optimize/src/main/java/org/eclipse/smarthome/io/rest/optimize/internal/ResourceFilterImpl.java b/bundles/org.openhab.core.io.rest.optimize/src/main/java/org/eclipse/smarthome/io/rest/optimize/internal/ResourceFilterImpl.java
new file mode 100644
index 0000000000..8b05a466e1
--- /dev/null
+++ b/bundles/org.openhab.core.io.rest.optimize/src/main/java/org/eclipse/smarthome/io/rest/optimize/internal/ResourceFilterImpl.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2010-2019 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.eclipse.smarthome.io.rest.optimize.internal;
+
+import static com.eclipsesource.jaxrs.publisher.ServiceProperties.PUBLISH;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.eclipsesource.jaxrs.publisher.ResourceFilter;
+
+/**
+ * Provides a filter for all classes/interfaces which are relevant in the context of JAX-RS.
+ *
+ * By default, this filter will allow every service outside of the org.eclipse.smarthome.**-Namespace to be parsed by
+ * the JAXR-RS implementation. To further optimize this, install a fragment which adds a "/res/whitelist.txt" file,
+ * containing one service interface or class per line like in the following example:
+ *
+ *
+ * {@code
+ * # My Custom Services
+ * org.example.foo
+ * org.example.bar
+ * # Another one
+ * org.example.test
+ * }
+ *
+ *
+ * If this file is present, no other services will be scanned and hence won't be available.
+ *
+ * @author Simon Kaufmann - initial contribution and API.
+ *
+ */
+public class ResourceFilterImpl implements ResourceFilter {
+
+ private final Logger logger = LoggerFactory.getLogger(ResourceFilterImpl.class);
+
+ /**
+ * All classes and interfaces which are considered to be relevant for JAX-RS.
+ */
+ private static final String[] WHITELIST = new String[] {
+ // JAX-RS
+ "javax.ws.rs.ext.MessageBodyReader", "javax.ws.rs.ext.MessageBodyWriter",
+ // openHAB
+ "org.eclipse.smarthome.io.rest.internal.filter.ProxyFilter",
+ "org.eclipse.smarthome.io.rest.internal.resources.RootResource",
+ "org.eclipse.smarthome.io.rest.JSONResponse$ExceptionMapper", "org.eclipse.smarthome.io.rest.RESTResource",
+ "org.eclipse.smarthome.io.rest.sse.internal.async.BlockingAsyncFeature",
+ "org.eclipse.smarthome.io.rest.sse.SseResource",
+ // SSE
+ "org.glassfish.jersey.media.sse.SseFeature",
+ "org.glassfish.jersey.server.monitoring.ApplicationEventListener" };
+
+ @Override
+ public Filter getFilter() {
+ String filterString = createFilter(WHITELIST);
+ try {
+ return FrameworkUtil.createFilter(filterString);
+ } catch (InvalidSyntaxException e) {
+ logger.error("Error creating RESTResource filter", e);
+ }
+ return null;
+ }
+
+ /**
+ * @param interfaces interface or class names
+ * @return filter string which matches if the class implements one of the interfaces or the name of the class is
+ * contained in interfaces
+ */
+ private String createFilter(String[] interfaces) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("(&");
+ builder.append("(|");
+ List whitelist = loadWhitelistExtension();
+ if (whitelist == null) {
+ logger.debug("No /res/whitelist.txt file found - scanning all unknown services");
+ builder.append("(!(" + Constants.OBJECTCLASS + "=org.eclipse.smarthome.*))");
+ } else {
+ logger.debug("Whitelist /res/whitelist.txt file found - restricting scanning of services");
+ whitelist.forEach(entry -> {
+ builder.append("(" + Constants.OBJECTCLASS + "=" + entry + ")");
+ });
+ }
+ for (String clazz : interfaces) {
+ builder.append("(" + Constants.OBJECTCLASS + "=" + clazz + ")");
+ }
+ builder.append(")");
+ builder.append("(!(" + PUBLISH + "=false)))");
+ return builder.toString();
+ }
+
+ private List loadWhitelistExtension() {
+ Enumeration entries = FrameworkUtil.getBundle(this.getClass()).findEntries("res", "whitelist.txt", false);
+ if (entries != null && entries.hasMoreElements()) {
+ URL url = entries.nextElement();
+ try (InputStream is = url.openStream()) {
+ return readWhitelistEntries(is);
+ } catch (IOException e) {
+ logger.warn("Error reading REST extension whitelist from {}", url, e);
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private List readWhitelistEntries(InputStream stream) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
+ List ret = new LinkedList<>();
+ String line = reader.readLine();
+ while (line != null) {
+ String trimmed = line.trim();
+ if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
+ ret.add(trimmed);
+ }
+ line = reader.readLine();
+ }
+ return ret;
+ }
+
+}