From 39f0e17002a08f74665df7a46ffbe060bdb28a15 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Wed, 31 Oct 2018 09:16:10 +0100 Subject: [PATCH] =?UTF-8?q?Introduced=20new=20ui.start=20bundle,=20which?= =?UTF-8?q?=20brings=20custom=20lifecycle=20status=20=E2=80=A6=20(#419)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Introduced new ui.start bundle, which brings custom lifecycle status HTTP pages as well as an 404 error page. Signed-off-by: Kai Kreuzer --- .../META-INF/MANIFEST.MF | 2 - .../openhab/ui/dashboard/DashboardReady.java | 19 ++ .../dashboard/internal/DashboardService.java | 20 +- bundles/org.openhab.ui.start/.classpath | 7 + bundles/org.openhab.ui.start/.project | 33 ++++ .../org.openhab.ui.start/META-INF/MANIFEST.MF | 21 +++ .../org.openhab.ui.start/OSGI-INF/.gitignore | 1 + bundles/org.openhab.ui.start/about.html | 37 ++++ bundles/org.openhab.ui.start/build.properties | 7 + bundles/org.openhab.ui.start/pages/404.html | 12 ++ .../org.openhab.ui.start/pages/status.html | 13 ++ bundles/org.openhab.ui.start/pom.xml | 14 ++ .../ui/start/internal/RootServlet.java | 173 ++++++++++++++++++ bundles/pom.xml | 1 + .../openhab-core/src/main/feature/feature.xml | 2 + features/p2/feature.xml | 6 + 16 files changed, 357 insertions(+), 11 deletions(-) create mode 100644 bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/DashboardReady.java create mode 100644 bundles/org.openhab.ui.start/.classpath create mode 100644 bundles/org.openhab.ui.start/.project create mode 100644 bundles/org.openhab.ui.start/META-INF/MANIFEST.MF create mode 100644 bundles/org.openhab.ui.start/OSGI-INF/.gitignore create mode 100644 bundles/org.openhab.ui.start/about.html create mode 100644 bundles/org.openhab.ui.start/build.properties create mode 100644 bundles/org.openhab.ui.start/pages/404.html create mode 100644 bundles/org.openhab.ui.start/pages/status.html create mode 100644 bundles/org.openhab.ui.start/pom.xml create mode 100644 bundles/org.openhab.ui.start/src/main/java/org/openhab/ui/start/internal/RootServlet.java diff --git a/bundles/org.openhab.ui.dashboard/META-INF/MANIFEST.MF b/bundles/org.openhab.ui.dashboard/META-INF/MANIFEST.MF index 48205f731..a0e023f54 100644 --- a/bundles/org.openhab.ui.dashboard/META-INF/MANIFEST.MF +++ b/bundles/org.openhab.ui.dashboard/META-INF/MANIFEST.MF @@ -1,6 +1,5 @@ Manifest-Version: 1.0 Automatic-Module-Name: org.openhab.ui.dashboard -Bundle-ActivationPolicy: lazy Bundle-ManifestVersion: 2 Bundle-Name: openHAB Dashboard UI Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -19,7 +18,6 @@ Import-Package: org.osgi.framework, org.osgi.service.cm, org.osgi.service.component, - org.osgi.service.component.annotations;resolution:=optional, org.osgi.service.http, org.slf4j Service-Component: OSGI-INF/*.xml diff --git a/bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/DashboardReady.java b/bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/DashboardReady.java new file mode 100644 index 000000000..e4e0a4905 --- /dev/null +++ b/bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/DashboardReady.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.ui.dashboard; + +/** + * This is a marker interface to declare that the dashboard is up and ready to be used. + * + * @author Kai Kreuzer - Initial contribution + * + */ +public interface DashboardReady { + +} diff --git a/bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/internal/DashboardService.java b/bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/internal/DashboardService.java index 8f37c44c6..9b3ad0e64 100644 --- a/bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/internal/DashboardService.java +++ b/bundles/org.openhab.ui.dashboard/src/main/java/org/openhab/ui/dashboard/internal/DashboardService.java @@ -24,10 +24,12 @@ import org.eclipse.smarthome.core.i18n.LocaleProvider; import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.net.HttpServiceUtil; import org.eclipse.smarthome.core.net.NetworkAddressService; +import org.openhab.ui.dashboard.DashboardReady; import org.openhab.ui.dashboard.DashboardTile; import org.osgi.framework.BundleContext; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.ComponentException; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -46,8 +48,8 @@ import org.slf4j.LoggerFactory; * @author Laurent Garnier - internationalization * @author Hilbrand Bouwkamp - internationalization */ -@Component(service = DashboardService.class, immediate = true, name = "org.openhab.dashboard") -public class DashboardService { +@Component(service = { DashboardService.class, DashboardReady.class }, immediate = true, name = "org.openhab.dashboard") +public class DashboardService implements DashboardReady { public static final String DASHBOARD_ALIAS = "/start"; @@ -166,10 +168,10 @@ public class DashboardService { try { indexTemplate = IOUtils.toString(index.openStream()); } catch (IOException e) { - throw new RuntimeException(e); + throw new ComponentException(e); } } else { - throw new RuntimeException("Cannot find index.html - failed to initialize Dashboard servlet"); + throw new ComponentException("Cannot find index.html - failed to initialize Dashboard servlet"); } URL entry = bundleContext.getBundle().getEntry("templates/entry.html"); @@ -177,10 +179,10 @@ public class DashboardService { try { entryTemplate = IOUtils.toString(entry.openStream()); } catch (IOException e) { - throw new RuntimeException(e); + throw new ComponentException(e); } } else { - throw new RuntimeException("Cannot find entry.html - failed to initialize Dashboard servlet"); + throw new ComponentException("Cannot find entry.html - failed to initialize Dashboard servlet"); } URL warn = bundleContext.getBundle().getEntry("templates/warn.html"); @@ -188,7 +190,7 @@ public class DashboardService { try { warnTemplate = IOUtils.toString(warn.openStream()); } catch (IOException e) { - throw new RuntimeException(e); + throw new ComponentException(e); } } else { throw new RuntimeException("Cannot find warn.html - failed to initialize Dashboard servlet"); @@ -199,10 +201,10 @@ public class DashboardService { try { setupTemplate = IOUtils.toString(setup.openStream()); } catch (IOException e) { - throw new RuntimeException(e); + throw new ComponentException(e); } } else { - throw new RuntimeException("Cannot find setup.html - failed to initialize Dashboard servlet"); + throw new ComponentException("Cannot find setup.html - failed to initialize Dashboard servlet"); } return new DashboardServlet(configurationAdmin, indexTemplate, entryTemplate, warnTemplate, setupTemplate, diff --git a/bundles/org.openhab.ui.start/.classpath b/bundles/org.openhab.ui.start/.classpath new file mode 100644 index 000000000..7f457fa41 --- /dev/null +++ b/bundles/org.openhab.ui.start/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.openhab.ui.start/.project b/bundles/org.openhab.ui.start/.project new file mode 100644 index 000000000..9ad58377d --- /dev/null +++ b/bundles/org.openhab.ui.start/.project @@ -0,0 +1,33 @@ + + + org.openhab.ui.start + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.openhab.ui.start/META-INF/MANIFEST.MF b/bundles/org.openhab.ui.start/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e442b0139 --- /dev/null +++ b/bundles/org.openhab.ui.start/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: org.openhab.ui.start +Bundle-ManifestVersion: 2 +Bundle-Name: openHAB Start UI +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-SymbolicName: org.openhab.ui.start +Bundle-Vendor: openHAB +Bundle-Version: 2.4.0.qualifier +Import-Package: + javax.servlet, + javax.servlet.http, + org.apache.commons.io, + org.eclipse.jdt.annotation;resolution:=optional, + org.openhab.ui.dashboard, + org.osgi.framework, + org.osgi.service.cm, + org.osgi.service.component, + org.osgi.service.http, + org.slf4j +Service-Component: OSGI-INF/*.xml +Bundle-ActivationPolicy: lazy diff --git a/bundles/org.openhab.ui.start/OSGI-INF/.gitignore b/bundles/org.openhab.ui.start/OSGI-INF/.gitignore new file mode 100644 index 000000000..b878e882a --- /dev/null +++ b/bundles/org.openhab.ui.start/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/*.xml diff --git a/bundles/org.openhab.ui.start/about.html b/bundles/org.openhab.ui.start/about.html new file mode 100644 index 000000000..2a7c838be --- /dev/null +++ b/bundles/org.openhab.ui.start/about.html @@ -0,0 +1,37 @@ + + + + +About + + +

About This Content

+ +

March 22, 2017

+

License

+ +

The openHAB community makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the openHAB community, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at openhab.org.

+ +

Third Party Content

+

The Content includes items that have been sourced from third parties as set out below. If you + did not receive this Content directly from the openHAB project, the following is provided + for informational purposes only, and you should look to the Redistributor's license for + terms and conditions of use.

+

+ Simple HttpErrorPages

+ HttpErrorPages obtained from Github repository under MIT License.
+

+ + \ No newline at end of file diff --git a/bundles/org.openhab.ui.start/build.properties b/bundles/org.openhab.ui.start/build.properties new file mode 100644 index 000000000..39c5e6747 --- /dev/null +++ b/bundles/org.openhab.ui.start/build.properties @@ -0,0 +1,7 @@ +output.. = target/classes/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + about.html,\ + pages/ +source.. = src/main/java/ diff --git a/bundles/org.openhab.ui.start/pages/404.html b/bundles/org.openhab.ui.start/pages/404.html new file mode 100644 index 000000000..c1694b07a --- /dev/null +++ b/bundles/org.openhab.ui.start/pages/404.html @@ -0,0 +1,12 @@ + + + + + + We've got some trouble | 404 - Resource not found + + + +

Resource not found | Error 404

The requested resource could not be found.

+ + diff --git a/bundles/org.openhab.ui.start/pages/status.html b/bundles/org.openhab.ui.start/pages/status.html new file mode 100644 index 000000000..522a16a4f --- /dev/null +++ b/bundles/org.openhab.ui.start/pages/status.html @@ -0,0 +1,13 @@ + + + + + + + ${message} + + + +

${message}

${submessage}

+ + diff --git a/bundles/org.openhab.ui.start/pom.xml b/bundles/org.openhab.ui.start/pom.xml new file mode 100644 index 000000000..e6ba16c3c --- /dev/null +++ b/bundles/org.openhab.ui.start/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + + org.openhab.core + pom-bundles + 2.4.0-SNAPSHOT + + + org.openhab.ui.start + + eclipse-plugin + openHAB Start UI + diff --git a/bundles/org.openhab.ui.start/src/main/java/org/openhab/ui/start/internal/RootServlet.java b/bundles/org.openhab.ui.start/src/main/java/org/openhab/ui/start/internal/RootServlet.java new file mode 100644 index 000000000..61980bdc7 --- /dev/null +++ b/bundles/org.openhab.ui.start/src/main/java/org/openhab/ui/start/internal/RootServlet.java @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2015-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.ui.start.internal; + +import java.io.IOException; +import java.net.URL; +import java.util.Properties; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.openhab.ui.dashboard.DashboardReady; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.SynchronousBundleListener; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.ComponentException; +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.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This servlet registers status (starting/stopping/updating) pages and serves the 404 page if system is started and an + * unknown url is called. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@Component(immediate = true) +public class RootServlet extends HttpServlet { + + private static final long serialVersionUID = -2091860295954594917L; + + private final Logger logger = LoggerFactory.getLogger(RootServlet.class); + + protected HttpService httpService; + + // an enumeration for the state the whole system is in + private enum LifeCycleState { + STARTING, + STARTED, + STOPPING, + UPDATING; + } + + private String page404; + private String pageStatus; + + private DashboardReady dashboardStarted; + private LifeCycleState lifecycleState = LifeCycleState.STARTING; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (dashboardStarted != null) { + // all is up and running + if (req.getRequestURI().equals("/")) { + resp.sendRedirect("/start/index"); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + resp.setContentType("text/html;charset=UTF-8"); + resp.getWriter().append(page404); + resp.getWriter().close(); + } + } else { + // report current system state + String message = null; + String subMessage = null; + switch (lifecycleState) { + case STARTING: + message = "openHAB is starting..."; + subMessage = "Please wait a moment!"; + break; + case UPDATING: + message = "openHAB is updating..."; + subMessage = "Please wait a moment!"; + break; + case STOPPING: + message = "openHAB is shutting down..."; + subMessage = "Please stand by."; + break; + default: + throw new IllegalStateException("Invalid system state " + lifecycleState); + } + resp.setContentType("text/html;charset=UTF-8"); + resp.getWriter().append(pageStatus.replace("${message}", message).replace("${submessage}", subMessage)); + resp.getWriter().close(); + } + } + + @Activate + protected void activate(ComponentContext context) { + try { + httpService.registerServlet("/", this, new Properties(), httpService.createDefaultHttpContext()); + } catch (ServletException | NamespaceException e) { + logger.error("Failed registering root servlet!", e); + } + URL notfound = context.getBundleContext().getBundle().getEntry("pages/404.html"); + if (notfound != null) { + try { + page404 = IOUtils.toString(notfound.openStream()); + } catch (IOException e) { + throw new ComponentException(e); + } + } else { + throw new ComponentException("Cannot find 404.html - failed to initialize root servlet"); + } + URL status = context.getBundleContext().getBundle().getEntry("pages/status.html"); + if (status != null) { + try { + pageStatus = IOUtils.toString(status.openStream()); + } catch (IOException e) { + throw new ComponentException(e); + } + } else { + throw new ComponentException("Cannot find status.html - failed to initialize root servlet"); + } + + // we can determine whether the whole framework is shutdown by listening to a STOPPING event for bundle 0. + Bundle systemBundle = context.getBundleContext().getBundle(0); + systemBundle.getBundleContext().addBundleListener(new SynchronousBundleListener() { + @Override + public void bundleChanged(final BundleEvent event) { + if (event.getBundle().getBundleId() == 0 && event.getType() == BundleEvent.STOPPING) { + lifecycleState = LifeCycleState.STOPPING; + } + } + }); + } + + @Deactivate + protected void deactivate() { + // reset, if this component is ever reused (should normally not be the case), it should be "starting" again. + lifecycleState = LifeCycleState.STARTING; + } + + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected void setDashboardStarted(DashboardReady dashboardStarted) { + this.dashboardStarted = dashboardStarted; + this.lifecycleState = LifeCycleState.STARTED; + } + + protected void unsetDashboardStarted(DashboardReady dashboardStarted) { + if (lifecycleState != LifeCycleState.STOPPING) { + lifecycleState = LifeCycleState.UPDATING; + } + this.dashboardStarted = null; + } + + @Reference + protected void setHttpService(HttpService httpService) { + this.httpService = httpService; + } + + protected void unsetHttpService(HttpService httpService) { + this.httpService = null; + } +} diff --git a/bundles/pom.xml b/bundles/pom.xml index 81f01e935..0cf6d160d 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -30,6 +30,7 @@ org.openhab.ui.classicui org.openhab.ui.homebuilder org.openhab.ui.paperui + org.openhab.ui.start diff --git a/features/openhab-core/src/main/feature/feature.xml b/features/openhab-core/src/main/feature/feature.xml index b2d223516..414cff959 100644 --- a/features/openhab-core/src/main/feature/feature.xml +++ b/features/openhab-core/src/main/feature/feature.xml @@ -32,6 +32,8 @@ openhab-transport-http shell wrapper + + mvn:org.openhab.core/org.openhab.ui.start/${project.version} mvn:org.openhab.core/org.openhab.core/${project.version} mvn:org.openhab.core/org.openhab.core.karaf/${project.version} mvn:org.openhab.core/org.openhab.io.sound/${project.version} diff --git a/features/p2/feature.xml b/features/p2/feature.xml index 075d7889e..5679122e3 100644 --- a/features/p2/feature.xml +++ b/features/p2/feature.xml @@ -86,4 +86,10 @@ version="0.0.0" unpack="false"/> +