From 973a72431cb42ad8d45f458bcb4fd2ade2f052df Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Mon, 20 Feb 2017 19:50:33 +0100 Subject: [PATCH 01/10] initial contribution of an IoT Market extension service Signed-off-by: Kai Kreuzer --- .../core/extensions/ExtensionResource.java | 4 +- .../.classpath | 7 + .../.project | 33 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../ESH-INF/config/config.xml | 32 ++ .../META-INF/MANIFEST.MF | 24 ++ .../OSGI-INF/MarketplaceExtensionService.xml | 13 + .../OSGI-INF/MarketplaceTemplateProvider.xml | 10 + .../about.html | 28 ++ .../build.properties | 6 + .../pom.xml | 16 + .../internal/MarketplaceExtension.java | 37 ++ .../internal/MarketplaceExtensionService.java | 404 ++++++++++++++++++ .../internal/MarketplaceProxy.java | 89 ++++ .../MarketplaceRuleTemplateProvider.java | 100 +++++ .../internal/MarketplaceXMLReader.java | 47 ++ .../marketplace/internal/model/Category.java | 21 + .../internal/model/Marketplace.java | 21 + .../marketplace/internal/model/Node.java | 56 +++ 19 files changed, 953 insertions(+), 2 deletions(-) create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.classpath create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.project create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.settings/org.eclipse.jdt.core.prefs create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/ESH-INF/config/config.xml create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceTemplateProvider.xml create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/about.html create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/pom.xml create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceXMLReader.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/extensions/ExtensionResource.java b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/extensions/ExtensionResource.java index 2f5ddf34a78..0a19efabfe7 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/extensions/ExtensionResource.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/extensions/ExtensionResource.java @@ -127,7 +127,7 @@ public Response getById(@HeaderParam("Accept-Language") @ApiParam(value = "langu } @POST - @Path("/{extensionId: [a-zA-Z_0-9-]*}/install") + @Path("/{extensionId: [a-zA-Z_0-9-:]*}/install") @ApiOperation(value = "Installs the extension with the given ID.") @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) public Response installExtension( @@ -149,7 +149,7 @@ public void run() { } @POST - @Path("/{extensionId: [a-zA-Z_0-9-]*}/uninstall") + @Path("/{extensionId: [a-zA-Z_0-9-:]*}/uninstall") @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) public Response uninstallExtension( final @PathParam("extensionId") @ApiParam(value = "extension ID", required = true) String extensionId) { diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.classpath b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.classpath new file mode 100644 index 00000000000..7f457fa4138 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.project b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.project new file mode 100644 index 00000000000..c707744c6b1 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.project @@ -0,0 +1,33 @@ + + + org.eclipse.smarthome.extensionservice.marketplace + + + + + + 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/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.settings/org.eclipse.jdt.core.prefs b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0c68a61dca8 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/ESH-INF/config/config.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/ESH-INF/config/config.xml new file mode 100644 index 00000000000..65b5f1a19df --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/ESH-INF/config/config.xml @@ -0,0 +1,32 @@ + + + + + + + Whether to make bindings from the marketplace available or not. + true + + + + Whether to make rule templates from the marketplace available or not. + true + + + + Only show entries that have a certain maturity. + 1 + + + + + + + + + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..e1d5e0bcd06 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF @@ -0,0 +1,24 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Eclipse SmartHome IoT Marketplace Extension Service +Bundle-SymbolicName: org.eclipse.smarthome.extensionservice.marketplace +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.9.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ClassPath: . +Import-Package: com.thoughtworks.xstream, + org.apache.commons.io, + org.apache.commons.lang, + org.eclipse.smarthome.automation.parser, + org.eclipse.smarthome.automation.template, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.xml.util, + org.eclipse.smarthome.core.common, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.events, + org.eclipse.smarthome.core.extension, + org.eclipse.smarthome.core.storage, + org.osgi.framework, + org.slf4j +Bundle-ActivationPolicy: lazy +Service-Component: OSGI-INF/*.xml diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml new file mode 100644 index 00000000000..bb0ccddd26e --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceTemplateProvider.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceTemplateProvider.xml new file mode 100644 index 00000000000..14e1ec751f7 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceTemplateProvider.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/about.html b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/about.html new file mode 100644 index 00000000000..1b9e722c419 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

<May 12, 2015>

+

License

+ +

The Eclipse Foundation 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 Eclipse Foundation, 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 http://www.eclipse.org.

+ + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties new file mode 100644 index 00000000000..43a0adc299f --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties @@ -0,0 +1,6 @@ +output.. = target/classes +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + about.html +source.. = src/main/java/ diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/pom.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/pom.xml new file mode 100644 index 00000000000..eb7b4b846c9 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + org.eclipse.smarthome.extensionservice + pom + 0.9.0-SNAPSHOT + + + org.eclipse.smarthome.extensionservice.marketplace + eclipse-plugin + + Eclipse SmartHome IoT Marketplace Extension Service + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java new file mode 100644 index 00000000000..2c67da3533b --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal; + +import org.eclipse.smarthome.core.extension.Extension; + +/** + * This is an {@link Extension}, which additionally holds a download url for its content. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class MarketplaceExtension extends Extension { + + public MarketplaceExtension(String id, String type, String label, String version, String link, boolean installed, + String description, String backgroundColor, String imageLink, String downloadUrl) { + super(id, type, label, version, link, installed, description, backgroundColor, imageLink); + this.downloadUrl = downloadUrl; + } + + // we mark it as transient, so that it isn't serialized through GSON + private transient String downloadUrl; + + /** + * returns the download url for the content of this extension + * + * @return a download url string + */ + public String getDownloadUrl() { + return downloadUrl; + } +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java new file mode 100644 index 00000000000..b0134da8bf5 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java @@ -0,0 +1,404 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.LineIterator; +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.automation.parser.ParsingException; +import org.eclipse.smarthome.core.events.Event; +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.extension.Extension; +import org.eclipse.smarthome.core.extension.ExtensionEventFactory; +import org.eclipse.smarthome.core.extension.ExtensionService; +import org.eclipse.smarthome.core.extension.ExtensionType; +import org.eclipse.smarthome.extensionservice.marketplace.internal.model.Node; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is an {@link ExtensionService}, which accesses the Eclipse IoT Marketplace and makes its content available as + * extensions. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class MarketplaceExtensionService implements ExtensionService { + + private static final String BINDING_FILE = "installedBindingsMap.csv"; + // constants used in marketplace nodes + private static final String MP_PACKAGETYPE_BINDING = "binding"; + private static final String MP_PACKAGETYPE_RULE_TEMPLATE = "rule_template"; + + // constants used to construct extension IDs + private static final String EXT_PREFIX = "market:"; + private static final String EXT_TYPE_RULE_TEMPLATE = "ruletemplate"; + private static final String EXT_TYPE_BINDING = "binding"; + + private final Logger logger = LoggerFactory.getLogger(MarketplaceExtensionService.class); + + private List extensionTypes; + private MarketplaceProxy proxy; + private EventPublisher eventPublisher; + private MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider; + private Map installedBindings; + + private boolean includeBindings = true; + private boolean includeRuleTemplates = true; + private int maturityLevel = 1; + + private BundleContext bundleContext; + + protected void activate(BundleContext bundleContext, Map config) { + this.bundleContext = bundleContext; + installedBindings = loadInstalledBindingsMap(); + this.proxy = new MarketplaceProxy(); + modified(config); + } + + protected void deactivate() { + this.proxy = null; + this.installedBindings = null; + this.bundleContext = null; + } + + protected void modified(Map config) { + Object bindingCfg = config.get("bindings"); + if (bindingCfg != null) { + this.includeBindings = bindingCfg.toString().equals(Boolean.TRUE.toString()); + } + Object ruleTemplateCfg = config.get("ruletemplates"); + if (ruleTemplateCfg != null) { + this.includeRuleTemplates = ruleTemplateCfg.toString().equals(Boolean.TRUE.toString()); + } + Object cfgMaturityLevel = config.get("maturity"); + if (cfgMaturityLevel != null) { + try { + this.maturityLevel = Integer.valueOf(cfgMaturityLevel.toString()); + } catch (NumberFormatException e) { + logger.warn("Ignoring invalid value '{}' for configuration parameter '{}'", cfgMaturityLevel.toString(), + "maturity"); + } + } + constructTypeList(); + } + + protected void setMarketplaceRuleTemplateProvider(MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider) { + this.marketplaceRuleTemplateProvider = marketplaceRuleTemplateProvider; + } + + protected void unsetMarketplaceRuleTemplateProvider( + MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider) { + this.marketplaceRuleTemplateProvider = null; + } + + protected void setEventPublisher(EventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + protected void unsetEventPublisher(EventPublisher eventPublisher) { + this.eventPublisher = null; + } + + @Override + public List getExtensions(Locale locale) { + List nodes = proxy.getNodes(); + List exts = new ArrayList<>(nodes.size()); + for (Node node : nodes) { + if (node.id == null) { + // workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=512493 + continue; + } + if (toMaturityLevel(node.status) < this.maturityLevel) { + continue; + } + if (!includeBindings && node.packagetypes.equals(MP_PACKAGETYPE_BINDING)) { + continue; + } + if (!includeRuleTemplates && node.packagetypes.equals(MP_PACKAGETYPE_RULE_TEMPLATE)) { + continue; + } + + Extension ext = convertToExtension(node); + if (ext != null) { + exts.add(ext); + } + } + return exts; + } + + private MarketplaceExtension convertToExtension(Node node) { + String extId = getExtensionId(node); + if (MP_PACKAGETYPE_BINDING.equals(node.packagetypes)) { + MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_BINDING, node.name, node.version, + node.supporturl, isInstalled(extId), node.shortdescription, null, node.image, node.updateurl); + return ext; + } else if (MP_PACKAGETYPE_RULE_TEMPLATE.equals(node.packagetypes)) { + String version = StringUtils.isNotEmpty(node.version) ? node.version : "1.0"; + MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_RULE_TEMPLATE, node.name, version, + node.supporturl, isInstalled(extId), node.shortdescription, null, node.image, node.updateurl); + return ext; + } else { + return null; + } + } + + @Override + public Extension getExtension(String id, Locale locale) { + for (Extension extension : getExtensions(locale)) { + if (extension.getId().equals(id)) { + return extension; + } + } + return null; + } + + @Override + public List getTypes(Locale locale) { + return extensionTypes; + } + + @Override + public void install(String extensionId) { + Extension ext = getExtension(extensionId, null); + if (ext instanceof MarketplaceExtension && !ext.isInstalled()) { + MarketplaceExtension mpExt = (MarketplaceExtension) ext; + String type = getType(extensionId); + switch (type) { + case EXT_TYPE_RULE_TEMPLATE: + installRuleTemplate(extensionId, mpExt); + break; + case EXT_TYPE_BINDING: + installBinding(extensionId, mpExt); + break; + default: + postFailureEvent(extensionId, "Id not known."); + break; + } + } + } + + @Override + public void uninstall(String extensionId) { + Extension ext = getExtension(extensionId, null); + if (ext != null && ext.isInstalled()) { + String type = getType(extensionId); + switch (type) { + case EXT_TYPE_RULE_TEMPLATE: + marketplaceRuleTemplateProvider.remove(extensionId); + postUninstalledEvent(extensionId); + break; + case EXT_TYPE_BINDING: + uninstallBinding(extensionId); + break; + default: + postFailureEvent(extensionId, "Id not known."); + break; + } + } + } + + private void installRuleTemplate(String extensionId, MarketplaceExtension mpExt) { + String url = mpExt.getDownloadUrl(); + try { + String template = getTemplate(url); + marketplaceRuleTemplateProvider.addTemplateAsJSON(extensionId, template); + postInstalledEvent(extensionId); + } catch (ParsingException e) { + postFailureEvent(extensionId, "Template is not valid."); + logger.error("Rule template from marketplace is invalid: {}", e.getMessage()); + } catch (IOException e) { + postFailureEvent(extensionId, "Template cannot be downloaded."); + logger.error("Rule template from marketplace cannot be downloaded: {}", e.getMessage()); + } + } + + private void installBinding(String extensionId, MarketplaceExtension mpExt) { + String url = mpExt.getDownloadUrl(); + try { + Bundle bundle = bundleContext.installBundle(url); + installedBindings.put(extensionId, bundle.getBundleId()); + persistInstalledBindingsMap(installedBindings); + postInstalledEvent(extensionId); + } catch (BundleException e) { + postFailureEvent(extensionId, "Binding cannot be installed: " + e.getMessage()); + } + } + + private void uninstallBinding(String extensionId) { + Long id = installedBindings.get(extensionId); + if (id != null) { + Bundle bundle = bundleContext.getBundle(id); + if (bundle != null) { + try { + bundle.uninstall(); + installedBindings.remove(extensionId); + persistInstalledBindingsMap(installedBindings); + postUninstalledEvent(extensionId); + } catch (BundleException e) { + postFailureEvent(extensionId, "Failed deinstalling binding: " + e.getMessage()); + } + } else { + // we do not have such a bundle, so let's remove it from our internal map + installedBindings.remove(extensionId); + persistInstalledBindingsMap(installedBindings); + postFailureEvent(extensionId, "Id not known."); + } + } else { + postFailureEvent(extensionId, "Id not known."); + } + } + + private String getTemplate(String urlString) throws IOException { + URL url = new URL(urlString); + return IOUtils.toString(url); + } + + private void postInstalledEvent(String extensionId) { + if (eventPublisher != null) { + Event event = ExtensionEventFactory.createExtensionInstalledEvent(extensionId); + eventPublisher.post(event); + } + } + + private void postUninstalledEvent(String extensionId) { + if (eventPublisher != null) { + Event event = ExtensionEventFactory.createExtensionUninstalledEvent(extensionId); + eventPublisher.post(event); + } + } + + private void postFailureEvent(String extensionId, String msg) { + if (eventPublisher != null) { + Event event = ExtensionEventFactory.createExtensionFailureEvent(extensionId, msg); + eventPublisher.post(event); + } + } + + private String getExtensionId(Node node) { + StringBuilder sb = new StringBuilder(EXT_PREFIX); + switch (node.packagetypes) { + case MP_PACKAGETYPE_RULE_TEMPLATE: + sb.append(EXT_TYPE_RULE_TEMPLATE).append("-"); + break; + case MP_PACKAGETYPE_BINDING: + sb.append(EXT_TYPE_BINDING).append("-"); + break; + default: + return null; + } + sb.append(node.id); // todo: remove illegal chars + return sb.toString(); + } + + private boolean isInstalled(String extensionId) { + String type = getType(extensionId); + if (type == null) { + return false; + } + switch (type) { + case EXT_TYPE_RULE_TEMPLATE: + return marketplaceRuleTemplateProvider.get(extensionId) != null; + case EXT_TYPE_BINDING: + return installedBindings.containsKey(extensionId); + default: + return false; + } + } + + private String getType(String extensionId) { + if (extensionId.startsWith(EXT_PREFIX)) { + String idWithoutPrefix = extensionId.substring(EXT_PREFIX.length()); + return idWithoutPrefix.substring(0, idWithoutPrefix.lastIndexOf('-')); + } else { + return null; + } + } + + private Map loadInstalledBindingsMap() { + File dataFile = bundleContext.getDataFile(BINDING_FILE); + if (dataFile != null && dataFile.exists()) { + try (FileReader reader = new FileReader(dataFile)) { + LineIterator lineIterator = IOUtils.lineIterator(reader); + Map map = new HashMap<>(); + while (lineIterator.hasNext()) { + String line = lineIterator.nextLine(); + String[] parts = line.split(";"); + try { + map.put(parts[0], Long.valueOf(parts[1])); + } catch (Exception e) { + logger.debug("Invalid line in file {} - ignoring it:\n{}", dataFile.getName(), line); + } + } + return map; + } catch (IOException e) { + logger.debug("File '{}' for installed bindings does not exist.", dataFile.getName()); + // ignore and just return an empty map + } + } + return new HashMap<>(); + } + + private synchronized void persistInstalledBindingsMap(Map map) { + File dataFile = bundleContext.getDataFile(BINDING_FILE); + if (dataFile != null) { + try (FileWriter writer = new FileWriter(dataFile)) { + for (Entry entry : map.entrySet()) { + writer.write(entry.getKey() + ";" + entry.getValue() + System.lineSeparator()); + } + } catch (IOException e) { + logger.warn("Failed writing file '{}': {}", dataFile.getName(), e.getMessage()); + } + } else { + logger.debug("System does not support bundle data files -> not persisting installed binding info"); + } + } + + private int toMaturityLevel(String maturity) { + switch (maturity) { + case "Alpha": + return 0; + case "Beta": + return 1; + case "Production/Stable": + return 2; + case "Mature": + return 3; + default: + logger.debug("Unknown maturity level value '{}' - using 'Alpha' instead.", maturity); + return 0; + } + } + + private void constructTypeList() { + ArrayList types = new ArrayList<>(2); + if (includeBindings) { + types.add(new ExtensionType(EXT_TYPE_BINDING, "Bindings")); + } + if (includeRuleTemplates) { + types.add(new ExtensionType(EXT_TYPE_RULE_TEMPLATE, "Rule Templates")); + } + extensionTypes = Collections.unmodifiableList(types); + } +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java new file mode 100644 index 00000000000..144d57f45ae --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.eclipse.smarthome.config.xml.util.XmlDocumentReader; +import org.eclipse.smarthome.core.common.ThreadPoolManager; +import org.eclipse.smarthome.extensionservice.marketplace.internal.model.Marketplace; +import org.eclipse.smarthome.extensionservice.marketplace.internal.model.Node; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provides local access to the market place content. Once started, it downloads the catalog and then makes + * its content available from memory. + * + * Note that there is no progressive/lazy browsing implemented yet, but the service downloads the whole catalog. + * Once the marketplace is filled with a lot of content, this will need to be addressed. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class MarketplaceProxy { + + private final Logger logger = LoggerFactory.getLogger(MarketplaceProxy.class); + + private final static String MP_URL = "https://marketplace.eclipse.org/taxonomy/term/4988%2C4396/api/p?client=org.eclipse.smarthome"; + private final URL url; + private Node[] cachedNodes = null; + private long refresh_interval = 3600; + private long retry_delay = 60; + + /** + * Creates a new instance, which immediately schedules a synchronization with the marketplace content. + */ + public MarketplaceProxy() { + try { + url = new URL(MP_URL); + ThreadPoolManager.getScheduledPool("marketplace").scheduleWithFixedDelay(() -> refresh(), 0, + refresh_interval, TimeUnit.SECONDS); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Something is very wrong - cannot instantiate URL " + MP_URL); + } + } + + /** + * returns the full list of marketplace nodes + * + * @return list of marketplace nodes + */ + public synchronized List getNodes() { + if (cachedNodes == null) { + XmlDocumentReader reader = new MarketplaceXMLReader(); + try { + Marketplace result = reader.readFromXML(url); + cachedNodes = result.categories[0].nodes; + } catch (Exception e) { + if (cachedNodes == null) { + logger.warn("Failed downloading Marketplace entries: {}", e.getMessage()); + logger.warn("Retrying again in a minute"); + ThreadPoolManager.getScheduledPool("marketplace").schedule(() -> refresh(), retry_delay, + TimeUnit.SECONDS); + } else { + logger.debug("Cannot access IoT Marketplace - will continue to use cached results: {}", + e.getMessage()); + } + } + } + return Arrays.asList(cachedNodes); + } + + /** + * Refreshes the local content by synchronizing with the remote marketplace. + */ + public synchronized void refresh() { + cachedNodes = null; + getNodes(); + } +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java new file mode 100644 index 00000000000..b7519e8ec4c --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Collection; +import java.util.Locale; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.eclipse.smarthome.automation.parser.Parser; +import org.eclipse.smarthome.automation.parser.ParsingException; +import org.eclipse.smarthome.automation.template.RuleTemplate; +import org.eclipse.smarthome.automation.template.RuleTemplateProvider; +import org.eclipse.smarthome.core.common.registry.DefaultAbstractManagedProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provides local access to the market place content. Once started, it downloads the catalog and then makes + * its content available from memory. + * + * Note that there is no progressive/lazy browsing implemented yet, but the service downloads the whole catalog. + * Once the marketplace is filled with a lot of content, this will need to be addressed. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class MarketplaceRuleTemplateProvider extends DefaultAbstractManagedProvider + implements RuleTemplateProvider { + + private final Logger logger = LoggerFactory.getLogger(MarketplaceRuleTemplateProvider.class); + + private Parser parser; + + @Override + public RuleTemplate getTemplate(String uid, Locale locale) { + return get(uid); + } + + @Override + public Collection getTemplates(Locale locale) { + return getAll(); + } + + @Override + protected String getKey(RuleTemplate element) { + return element.getUID(); + } + + @Override + protected String getStorageName() { + return "org.eclipse.smarthome.extensionservice.marketplace.RuleTemplates"; + } + + @Override + protected String keyToString(String key) { + return key; + } + + protected void setParser(Parser parser) { + this.parser = parser; + } + + protected void unseteParser(Parser parser) { + this.parser = null; + } + + /** + * This adds a new rule template to the persistent storage. + * + * @param uid the UID to be used for the template + * @param json the template content as a json string + * + * @throws ParsingException if the content cannot be parsed correctly + */ + public void addTemplateAsJSON(String uid, String json) throws ParsingException { + try (InputStreamReader isr = new InputStreamReader(IOUtils.toInputStream(json))) { + Set templates = parser.parse(isr); + if (templates.size() != 1) { + throw new IllegalArgumentException("JSON must contain exactly one template!"); + } else { + RuleTemplate entry = templates.iterator().next(); + RuleTemplate template = new RuleTemplate(uid, entry.getLabel(), entry.getDescription(), entry.getTags(), + entry.getTriggers(), entry.getConditions(), entry.getActions(), + entry.getConfigurationDescriptions(), entry.getVisibility()); + add(template); + } + } catch (IOException e) { + logger.error("Cannot close input stream.", e); + } + } + +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceXMLReader.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceXMLReader.java new file mode 100644 index 00000000000..376b47b85c2 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceXMLReader.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal; + +import org.eclipse.smarthome.config.xml.util.XmlDocumentReader; +import org.eclipse.smarthome.extensionservice.marketplace.internal.model.Category; +import org.eclipse.smarthome.extensionservice.marketplace.internal.model.Marketplace; +import org.eclipse.smarthome.extensionservice.marketplace.internal.model.Node; + +import com.thoughtworks.xstream.XStream; + +/** + * This is the XML reader for the marketplace content. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class MarketplaceXMLReader extends XmlDocumentReader { + + public MarketplaceXMLReader() { + super.setClassLoader(Marketplace.class.getClassLoader()); + } + + @Override + public void registerConverters(XStream xstream) { + } + + @Override + public void registerAliases(XStream xstream) { + xstream.alias("marketplace", Marketplace.class); + xstream.addImplicitArray(Marketplace.class, "categories"); + xstream.alias("category", Category.class); + xstream.addImplicitArray(Category.class, "nodes"); + xstream.alias("node", Node.class); + xstream.aliasAttribute(Node.class, "id", "id"); + xstream.aliasAttribute(Node.class, "name", "name"); + + // ignore what we do not know + xstream.ignoreUnknownElements(); + } + +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java new file mode 100644 index 00000000000..19e1002fdf0 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal.model; + +/** + * This is a category that holds the nodes as individual entries of the marketplace. + * + * @author Kai Kreuzer - Initial contribution and API + */ +public class Category { + + /** + * The category of the marketplace + */ + public Node[] nodes; +} \ No newline at end of file diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java new file mode 100644 index 00000000000..a513633343f --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal.model; + +/** + * This is the parent object that holds the category of the market. + * + * @author Kai Kreuzer - Initial contribution and API + */ +public class Marketplace { + + /** + * The category of the marketplace + */ + public Category[] categories; +} \ No newline at end of file diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java new file mode 100644 index 00000000000..4463d5189c0 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal.model; + +import java.util.Set; + +/** + * A node represents an entry on the marketplace. + * + * @author Kai Kreuzer - Initial contribution and API + */ +public class Node { + + public String id; + + public String name; + + public Integer favorited; + + public Integer installsTotal; + + public Integer installsRecent; + + public Set tags; + + public String shortdescription; + + public String body; + + public Long created; + + public Long changed; + + public String image; + + public String license; + + public String companyname; + + public String status; + + public String version; + + public String supporturl; + + public String packagetypes; + + public String packageformat; + + public String updateurl; +} \ No newline at end of file From a00bcf5581dae29c4862627d71e38c9b3f18a678 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Wed, 1 Mar 2017 15:49:08 +0100 Subject: [PATCH 02/10] addressed review comments Signed-off-by: Kai Kreuzer --- .../META-INF/MANIFEST.MF | 2 +- .../internal/MarketplaceExtension.java | 6 +- .../internal/MarketplaceExtensionService.java | 102 +++++++++++------- .../MarketplaceRuleTemplateProvider.java | 2 +- .../marketplace/internal/model/Category.java | 2 +- .../internal/model/Marketplace.java | 2 +- .../marketplace/internal/model/Node.java | 2 +- 7 files changed, 69 insertions(+), 49 deletions(-) diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF index e1d5e0bcd06..2d4b88ffe5d 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Eclipse SmartHome IoT Marketplace Extension Service -Bundle-SymbolicName: org.eclipse.smarthome.extensionservice.marketplace +Bundle-SymbolicName: org.eclipse.smarthome.extensionservice.marketplace;singleton:=true Bundle-Vendor: Eclipse.org/SmartHome Bundle-Version: 0.9.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java index 2c67da3533b..c462c765e7b 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java @@ -17,15 +17,15 @@ */ public class MarketplaceExtension extends Extension { + // we mark it as transient, so that it isn't serialized through GSON + private transient String downloadUrl; + public MarketplaceExtension(String id, String type, String label, String version, String link, boolean installed, String description, String backgroundColor, String imageLink, String downloadUrl) { super(id, type, label, version, link, installed, description, backgroundColor, imageLink); this.downloadUrl = downloadUrl; } - // we mark it as transient, so that it isn't serialized through GSON - private transient String downloadUrl; - /** * returns the download url for the content of this extension * diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java index b0134da8bf5..627726cd24c 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java @@ -182,40 +182,60 @@ public List getTypes(Locale locale) { @Override public void install(String extensionId) { Extension ext = getExtension(extensionId, null); - if (ext instanceof MarketplaceExtension && !ext.isInstalled()) { - MarketplaceExtension mpExt = (MarketplaceExtension) ext; - String type = getType(extensionId); - switch (type) { - case EXT_TYPE_RULE_TEMPLATE: - installRuleTemplate(extensionId, mpExt); - break; - case EXT_TYPE_BINDING: - installBinding(extensionId, mpExt); - break; - default: - postFailureEvent(extensionId, "Id not known."); - break; + if (ext instanceof MarketplaceExtension) { + if (!ext.isInstalled()) { + MarketplaceExtension mpExt = (MarketplaceExtension) ext; + String type = getType(extensionId); + if (type != null) { + switch (type) { + case EXT_TYPE_RULE_TEMPLATE: + installRuleTemplate(extensionId, mpExt); + break; + case EXT_TYPE_BINDING: + installBinding(extensionId, mpExt); + break; + default: + postFailureEvent(extensionId, "Extension type " + type + " not known."); + break; + } + } else { + postFailureEvent(extensionId, "Extension not known."); + } + } else { + postFailureEvent(extensionId, "Extension is already installed."); } + } else { + postFailureEvent(extensionId, "Extension not known."); } } @Override public void uninstall(String extensionId) { Extension ext = getExtension(extensionId, null); - if (ext != null && ext.isInstalled()) { - String type = getType(extensionId); - switch (type) { - case EXT_TYPE_RULE_TEMPLATE: - marketplaceRuleTemplateProvider.remove(extensionId); - postUninstalledEvent(extensionId); - break; - case EXT_TYPE_BINDING: - uninstallBinding(extensionId); - break; - default: - postFailureEvent(extensionId, "Id not known."); - break; + if (ext != null) { + if (ext.isInstalled()) { + String type = getType(extensionId); + if (type != null) { + switch (type) { + case EXT_TYPE_RULE_TEMPLATE: + marketplaceRuleTemplateProvider.remove(extensionId); + postUninstalledEvent(extensionId); + break; + case EXT_TYPE_BINDING: + uninstallBinding(extensionId); + break; + default: + postFailureEvent(extensionId, "Extension type " + type + " not known."); + break; + } + } else { + postFailureEvent(extensionId, "Extension not known."); + } + } else { + postFailureEvent(extensionId, "Extension is not installed."); } + } else { + postFailureEvent(extensionId, "Extension not known."); } } @@ -276,24 +296,19 @@ private String getTemplate(String urlString) throws IOException { } private void postInstalledEvent(String extensionId) { - if (eventPublisher != null) { - Event event = ExtensionEventFactory.createExtensionInstalledEvent(extensionId); - eventPublisher.post(event); - } + Event event = ExtensionEventFactory.createExtensionInstalledEvent(extensionId); + eventPublisher.post(event); } private void postUninstalledEvent(String extensionId) { - if (eventPublisher != null) { - Event event = ExtensionEventFactory.createExtensionUninstalledEvent(extensionId); - eventPublisher.post(event); - } + Event event = ExtensionEventFactory.createExtensionUninstalledEvent(extensionId); + eventPublisher.post(event); } private void postFailureEvent(String extensionId, String msg) { - if (eventPublisher != null) { - Event event = ExtensionEventFactory.createExtensionFailureEvent(extensionId, msg); - eventPublisher.post(event); - } + Event event = ExtensionEventFactory.createExtensionFailureEvent(extensionId, msg); + eventPublisher.post(event); + } private String getExtensionId(Node node) { @@ -345,9 +360,14 @@ private Map loadInstalledBindingsMap() { while (lineIterator.hasNext()) { String line = lineIterator.nextLine(); String[] parts = line.split(";"); - try { - map.put(parts[0], Long.valueOf(parts[1])); - } catch (Exception e) { + if (parts.length == 2) { + try { + map.put(parts[0], Long.valueOf(parts[1])); + } catch (NumberFormatException e) { + logger.debug("Cannot parse '{}' as a number in file {} - ignoring it.", parts[1], + dataFile.getName()); + } + } else { logger.debug("Invalid line in file {} - ignoring it:\n{}", dataFile.getName(), line); } } diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java index b7519e8ec4c..5c42c209ad8 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java @@ -68,7 +68,7 @@ protected void setParser(Parser parser) { this.parser = parser; } - protected void unseteParser(Parser parser) { + protected void unsetParser(Parser parser) { this.parser = null; } diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java index 19e1002fdf0..ffed480dfd2 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Category.java @@ -18,4 +18,4 @@ public class Category { * The category of the marketplace */ public Node[] nodes; -} \ No newline at end of file +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java index a513633343f..d92b2fc6726 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Marketplace.java @@ -18,4 +18,4 @@ public class Marketplace { * The category of the marketplace */ public Category[] categories; -} \ No newline at end of file +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java index 4463d5189c0..eec64b92f28 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/model/Node.java @@ -53,4 +53,4 @@ public class Node { public String packageformat; public String updateurl; -} \ No newline at end of file +} From 6f9fedc18205661087dbfc9dcb5960fa13763f6d Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Wed, 1 Mar 2017 17:14:22 +0100 Subject: [PATCH 03/10] added content validation Signed-off-by: Kai Kreuzer --- .../internal/MarketplaceExtensionService.java | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java index 627726cd24c..88da4f09296 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java @@ -19,6 +19,7 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; @@ -61,15 +62,17 @@ public class MarketplaceExtensionService implements ExtensionService { private List extensionTypes; private MarketplaceProxy proxy; private EventPublisher eventPublisher; + private BundleContext bundleContext; private MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider; private Map installedBindings; + private Pattern labelPattern = Pattern.compile("<.*>"); // checks for the existence of any xml element + private Pattern descriptionPattern = Pattern.compile("<(javascript|div|font)"); // checks for the existence of some + // invalid elements private boolean includeBindings = true; private boolean includeRuleTemplates = true; private int maturityLevel = 1; - private BundleContext bundleContext; - protected void activate(BundleContext bundleContext, Map config) { this.bundleContext = bundleContext; installedBindings = loadInstalledBindingsMap(); @@ -150,14 +153,22 @@ public List getExtensions(Locale locale) { private MarketplaceExtension convertToExtension(Node node) { String extId = getExtensionId(node); + + String name = node.name; + String desc = node.shortdescription; + + if (!validName(name) || !validDescription(desc)) { + logger.debug("Ignoring node {} due to invalid content.", node.id); + return null; + } if (MP_PACKAGETYPE_BINDING.equals(node.packagetypes)) { - MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_BINDING, node.name, node.version, - node.supporturl, isInstalled(extId), node.shortdescription, null, node.image, node.updateurl); + MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_BINDING, name, node.version, + node.supporturl, isInstalled(extId), desc, null, node.image, node.updateurl); return ext; } else if (MP_PACKAGETYPE_RULE_TEMPLATE.equals(node.packagetypes)) { String version = StringUtils.isNotEmpty(node.version) ? node.version : "1.0"; - MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_RULE_TEMPLATE, node.name, version, - node.supporturl, isInstalled(extId), node.shortdescription, null, node.image, node.updateurl); + MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_RULE_TEMPLATE, name, version, + node.supporturl, isInstalled(extId), desc, null, node.image, node.updateurl); return ext; } else { return null; @@ -323,7 +334,7 @@ private String getExtensionId(Node node) { default: return null; } - sb.append(node.id); // todo: remove illegal chars + sb.append(node.id.replaceAll("[^a-zA-Z0-9_]", "")); return sb.toString(); } @@ -411,6 +422,14 @@ private int toMaturityLevel(String maturity) { } } + private boolean validName(String name) { + return !labelPattern.matcher(name).find(); + } + + private boolean validDescription(String desc) { + return !descriptionPattern.matcher(desc).find(); + } + private void constructTypeList() { ArrayList types = new ArrayList<>(2); if (includeBindings) { From 84b749e6aba73f947ffc7874018963ab7d1ebe24 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Fri, 3 Mar 2017 15:41:17 +0100 Subject: [PATCH 04/10] refactored code to make it extensible and moved automation (rule template) support to a separate bundle Signed-off-by: Kai Kreuzer --- .../.classpath | 7 + .../.project | 33 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../META-INF/MANIFEST.MF | 20 ++ .../MarketplaceAutomationExtension.xml | 8 + .../OSGI-INF/MarketplaceTemplateProvider.xml | 4 +- .../about.html | 28 ++ .../build.properties | 6 + .../pom.xml | 16 + .../internal/AutomationExtensionHandler.java | 82 +++++ .../MarketplaceRuleTemplateProvider.java | 2 +- .../META-INF/MANIFEST.MF | 3 +- .../OSGI-INF/MarketplaceBindingExtension.xml | 7 + .../OSGI-INF/MarketplaceExtensionService.xml | 2 +- .../build.properties | 3 +- .../{internal => }/MarketplaceExtension.java | 31 +- .../MarketplaceExtensionHandler.java | 58 ++++ .../MarketplaceHandlerException.java | 28 ++ .../internal/BindingExtensionHandler.java | 151 +++++++++ .../internal/MarketplaceExtensionService.java | 306 +++++------------- 20 files changed, 568 insertions(+), 234 deletions(-) create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.classpath create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.project create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.settings/org.eclipse.jdt.core.prefs create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/META-INF/MANIFEST.MF create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/OSGI-INF/MarketplaceAutomationExtension.xml rename extensions/extensionservice/{org.eclipse.smarthome.extensionservice.marketplace => org.eclipse.smarthome.extensionservice.marketplace.automation}/OSGI-INF/MarketplaceTemplateProvider.xml (86%) create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/about.html create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/build.properties create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/pom.xml create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/AutomationExtensionHandler.java rename extensions/extensionservice/{org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace => org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation}/internal/MarketplaceRuleTemplateProvider.java (97%) create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceBindingExtension.xml rename extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/{internal => }/MarketplaceExtension.java (50%) create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceExtensionHandler.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceHandlerException.java create mode 100644 extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.classpath b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.classpath new file mode 100644 index 00000000000..7f457fa4138 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.project b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.project new file mode 100644 index 00000000000..99800425e45 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.project @@ -0,0 +1,33 @@ + + + org.eclipse.smarthome.extensionservice.marketplace.automation + + + + + + 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/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.settings/org.eclipse.jdt.core.prefs b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0c68a61dca8 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/META-INF/MANIFEST.MF b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..7f0ecc8e3b4 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Eclipse SmartHome IoT Marketplace Extension Automation Support +Bundle-SymbolicName: org.eclipse.smarthome.extensionservice.marketplace.automation;singleton:=true +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.9.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ClassPath: . +Import-Package: org.apache.commons.io, + org.eclipse.smarthome.automation.parser, + org.eclipse.smarthome.automation.template, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.extension, + org.eclipse.smarthome.core.storage, + org.eclipse.smarthome.extensionservice.marketplace, + org.osgi.framework, + org.slf4j +Bundle-ActivationPolicy: lazy +Service-Component: OSGI-INF/*.xml diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/OSGI-INF/MarketplaceAutomationExtension.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/OSGI-INF/MarketplaceAutomationExtension.xml new file mode 100644 index 00000000000..645a8941c87 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/OSGI-INF/MarketplaceAutomationExtension.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceTemplateProvider.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/OSGI-INF/MarketplaceTemplateProvider.xml similarity index 86% rename from extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceTemplateProvider.xml rename to extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/OSGI-INF/MarketplaceTemplateProvider.xml index 14e1ec751f7..9848d222161 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceTemplateProvider.xml +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/OSGI-INF/MarketplaceTemplateProvider.xml @@ -1,8 +1,8 @@ - + - + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/about.html b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/about.html new file mode 100644 index 00000000000..1b9e722c419 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

<May 12, 2015>

+

License

+ +

The Eclipse Foundation 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 Eclipse Foundation, 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 http://www.eclipse.org.

+ + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/build.properties b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/build.properties new file mode 100644 index 00000000000..43a0adc299f --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/build.properties @@ -0,0 +1,6 @@ +output.. = target/classes +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + about.html +source.. = src/main/java/ diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/pom.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/pom.xml new file mode 100644 index 00000000000..9f163ad4087 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + org.eclipse.smarthome.extensionservice + pom + 0.9.0-SNAPSHOT + + + org.eclipse.smarthome.extensionservice.marketplace.automation + eclipse-plugin + + Eclipse SmartHome IoT Marketplace Extension Automation Support + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/AutomationExtensionHandler.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/AutomationExtensionHandler.java new file mode 100644 index 00000000000..e55ea003924 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/AutomationExtensionHandler.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.automation.internal; + +import java.io.IOException; +import java.net.URL; + +import org.apache.commons.io.IOUtils; +import org.eclipse.smarthome.automation.template.RuleTemplateProvider; +import org.eclipse.smarthome.core.storage.Storage; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceExtension; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceExtensionHandler; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceHandlerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A {@link MarketplaceExtensionHandler} implementation, which handles rule templates as JSON files and installs + * them by adding them to a {@link Storage}. The templates are then served from this storage through a dedicated + * {@link RuleTemplateProvider}. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class AutomationExtensionHandler implements MarketplaceExtensionHandler { + + private final Logger logger = LoggerFactory.getLogger(AutomationExtensionHandler.class); + + private MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider; + + protected void setMarketplaceRuleTemplateProvider(MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider) { + this.marketplaceRuleTemplateProvider = marketplaceRuleTemplateProvider; + } + + protected void unsetMarketplaceRuleTemplateProvider( + MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider) { + this.marketplaceRuleTemplateProvider = null; + } + + @Override + public boolean supports(MarketplaceExtension ext) { + // we support only rule templates in JSON format so far + return ext.getType().equals(MarketplaceExtension.EXT_TYPE_RULE_TEMPLATE) + && ext.getPackageFormat().equals(MarketplaceExtension.EXT_FORMAT_JSON); + } + + @Override + public boolean isInstalled(MarketplaceExtension ext) { + return marketplaceRuleTemplateProvider.get(ext.getId()) != null; + } + + @Override + public void install(MarketplaceExtension ext) throws MarketplaceHandlerException { + String url = ext.getDownloadUrl(); + try { + String template = getTemplate(url); + marketplaceRuleTemplateProvider.addTemplateAsJSON(ext.getId(), template); + } catch (IOException e) { + logger.error("Rule template from marketplace cannot be downloaded: {}", e.getMessage()); + throw new MarketplaceHandlerException("Template cannot be downloaded."); + } catch (Exception e) { + logger.error("Rule template from marketplace is invalid: {}", e.getMessage()); + throw new MarketplaceHandlerException("Template is not valid."); + } + } + + @Override + public void uninstall(MarketplaceExtension ext) throws MarketplaceHandlerException { + marketplaceRuleTemplateProvider.remove(ext.getId()); + } + + private String getTemplate(String urlString) throws IOException { + URL url = new URL(urlString); + return IOUtils.toString(url); + } + +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java similarity index 97% rename from extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java rename to extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java index 5c42c209ad8..b1a578a4245 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceRuleTemplateProvider.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.smarthome.extensionservice.marketplace.internal; +package org.eclipse.smarthome.extensionservice.marketplace.automation.internal; import java.io.IOException; import java.io.InputStreamReader; diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF index 2d4b88ffe5d..9d7e526315c 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF @@ -9,8 +9,6 @@ Bundle-ClassPath: . Import-Package: com.thoughtworks.xstream, org.apache.commons.io, org.apache.commons.lang, - org.eclipse.smarthome.automation.parser, - org.eclipse.smarthome.automation.template, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.xml.util, org.eclipse.smarthome.core.common, @@ -22,3 +20,4 @@ Import-Package: com.thoughtworks.xstream, org.slf4j Bundle-ActivationPolicy: lazy Service-Component: OSGI-INF/*.xml +Export-Package: org.eclipse.smarthome.extensionservice.marketplace diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceBindingExtension.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceBindingExtension.xml new file mode 100644 index 00000000000..913b00df0c1 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceBindingExtension.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml index bb0ccddd26e..a559902bffe 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/OSGI-INF/MarketplaceExtensionService.xml @@ -5,7 +5,7 @@ - + diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties index 43a0adc299f..831dfb342a0 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/build.properties @@ -2,5 +2,6 @@ output.. = target/classes bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - about.html + about.html,\ + ESH-INF/ source.. = src/main/java/ diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceExtension.java similarity index 50% rename from extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java rename to extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceExtension.java index c462c765e7b..4472e189200 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtension.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceExtension.java @@ -5,25 +5,38 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.smarthome.extensionservice.marketplace.internal; +package org.eclipse.smarthome.extensionservice.marketplace; import org.eclipse.smarthome.core.extension.Extension; /** - * This is an {@link Extension}, which additionally holds a download url for its content. + * This is an {@link Extension}, which additionally holds the package format and a download url for its content. * * @author Kai Kreuzer - Initial contribution and API * */ public class MarketplaceExtension extends Extension { - // we mark it as transient, so that it isn't serialized through GSON + // constants used to construct extension IDs + public static final String EXT_PREFIX = "market:"; + + // extension types from marketplace + public static final String EXT_TYPE_RULE_TEMPLATE = "ruletemplate"; + public static final String EXT_TYPE_BINDING = "binding"; + + // extension package formats from marketplace + public static final String EXT_FORMAT_BUNDLE = "bundle"; + public static final String EXT_FORMAT_JSON = "json"; + + // we mark them as transient, so that they are not serialized through GSON private transient String downloadUrl; + private transient String packageFormat; public MarketplaceExtension(String id, String type, String label, String version, String link, boolean installed, - String description, String backgroundColor, String imageLink, String downloadUrl) { + String description, String backgroundColor, String imageLink, String downloadUrl, String packageFormat) { super(id, type, label, version, link, installed, description, backgroundColor, imageLink); this.downloadUrl = downloadUrl; + this.packageFormat = packageFormat; } /** @@ -34,4 +47,14 @@ public MarketplaceExtension(String id, String type, String label, String version public String getDownloadUrl() { return downloadUrl; } + + /** + * returns the package format of this extension + * + * @return package format of the extension + */ + public String getPackageFormat() { + return packageFormat; + } + } diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceExtensionHandler.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceExtensionHandler.java new file mode 100644 index 00000000000..89179877378 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceExtensionHandler.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace; + +/** + * This interface can be implemented by services that want to register as handlers for specific marketplace extension + * types and formats. + * In a system there should always only be exactly one handler responsible for a given type+format combination. If + * multiple handers support it, it is undefined which one will be called. + * This mechanism allows solutions to add support for specific formats (e.g. Karaf features) that are not supported by + * ESH out of the box. + * It also allows to decide which extension types are made available at all. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public interface MarketplaceExtensionHandler { + + /** + * Tells whether this handler supports a given extension. + * + * @param ext the extension in question + * @return true, if the extension is supported, false otherwise + */ + boolean supports(MarketplaceExtension ext); + + /** + * Tells whether a given extension is currently installed. + * Note: This method is only called, if the hander claimed support for the extension before. + * + * @param ext the extension in question + * @return true, if the extension is installed, false otherwise + */ + boolean isInstalled(MarketplaceExtension ext); + + /** + * Installs a given extension. + * Note: This method is only called, if the hander claimed support for the extension before. + * + * @param ext the extension to install + * @throws MarketplaceHandlerException if the installation failed for some reason + */ + void install(MarketplaceExtension ext) throws MarketplaceHandlerException; + + /** + * Uninstalls a given extension. + * Note: This method is only called, if the hander claimed support for the extension before. + * + * @param ext the extension to uninstall + * @throws MarketplaceHandlerException if the uninstallation failed for some reason + */ + void uninstall(MarketplaceExtension ext) throws MarketplaceHandlerException; +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceHandlerException.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceHandlerException.java new file mode 100644 index 00000000000..70083ae7bb7 --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/MarketplaceHandlerException.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace; + +/** + * This is an exception that can be thrown by {@link MarketplaceExtensionHandler}s if some operation fails. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class MarketplaceHandlerException extends Exception { + + private static final long serialVersionUID = -5652014141471618161L; + + /** + * Main constructor + * + * @param message A message describing the issue + */ + public MarketplaceHandlerException(String message) { + super(message); + } +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java new file mode 100644 index 00000000000..f73e1cdd34e --- /dev/null +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.extensionservice.marketplace.internal; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.LineIterator; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceExtension; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceExtensionHandler; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceHandlerException; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A {@link MarketplaceExtensionHandler} implementation, which handles bindings as jar files (OSGi bundles) and installs + * them through the standard OSGi bundle installation mechanism. + * The information, which installed bundle corresponds to which extension is written to a file in the bundle's data + * store. It is therefore wiped together with the bundles upon an OSGi "clean". + * We might want to move this class into a separate bundle in future, when we add support for further extension types. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class BindingExtensionHandler implements MarketplaceExtensionHandler { + + private static final String BINDING_FILE = "installedBindingsMap.csv"; + + private final Logger logger = LoggerFactory.getLogger(BindingExtensionHandler.class); + + private Map installedBindings; + + private BundleContext bundleContext; + + protected void activate(BundleContext bundleContext, Map config) { + this.bundleContext = bundleContext; + installedBindings = loadInstalledBindingsMap(); + } + + protected void deactivate() { + this.installedBindings = null; + this.bundleContext = null; + } + + @Override + public boolean supports(MarketplaceExtension ext) { + // we support only bindings as pure OSGi bundles + return ext.getType().equals(MarketplaceExtension.EXT_TYPE_BINDING) + && ext.getPackageFormat().equals(MarketplaceExtension.EXT_FORMAT_BUNDLE); + } + + @Override + public boolean isInstalled(MarketplaceExtension ext) { + return installedBindings.containsKey(ext.getId()); + } + + @Override + public void install(MarketplaceExtension ext) throws MarketplaceHandlerException { + String url = ext.getDownloadUrl(); + try { + Bundle bundle = bundleContext.installBundle(url); + installedBindings.put(ext.getId(), bundle.getBundleId()); + persistInstalledBindingsMap(installedBindings); + } catch (BundleException e) { + throw new MarketplaceHandlerException("Binding cannot be installed: " + e.getMessage()); + } + } + + @Override + public void uninstall(MarketplaceExtension ext) throws MarketplaceHandlerException { + Long id = installedBindings.get(ext.getId()); + if (id != null) { + Bundle bundle = bundleContext.getBundle(id); + if (bundle != null) { + try { + bundle.uninstall(); + installedBindings.remove(ext.getId()); + persistInstalledBindingsMap(installedBindings); + } catch (BundleException e) { + throw new MarketplaceHandlerException("Failed deinstalling binding: " + e.getMessage()); + } + } else { + // we do not have such a bundle, so let's remove it from our internal map + installedBindings.remove(ext.getId()); + persistInstalledBindingsMap(installedBindings); + throw new MarketplaceHandlerException("Id not known."); + } + } else { + throw new MarketplaceHandlerException("Id not known."); + } + } + + private Map loadInstalledBindingsMap() { + File dataFile = bundleContext.getDataFile(BINDING_FILE); + if (dataFile != null && dataFile.exists()) { + try (FileReader reader = new FileReader(dataFile)) { + LineIterator lineIterator = IOUtils.lineIterator(reader); + Map map = new HashMap<>(); + while (lineIterator.hasNext()) { + String line = lineIterator.nextLine(); + String[] parts = line.split(";"); + if (parts.length == 2) { + try { + map.put(parts[0], Long.valueOf(parts[1])); + } catch (NumberFormatException e) { + logger.debug("Cannot parse '{}' as a number in file {} - ignoring it.", parts[1], + dataFile.getName()); + } + } else { + logger.debug("Invalid line in file {} - ignoring it:\n{}", dataFile.getName(), line); + } + } + return map; + } catch (IOException e) { + logger.debug("File '{}' for installed bindings does not exist.", dataFile.getName()); + // ignore and just return an empty map + } + } + return new HashMap<>(); + } + + private synchronized void persistInstalledBindingsMap(Map map) { + File dataFile = bundleContext.getDataFile(BINDING_FILE); + if (dataFile != null) { + try (FileWriter writer = new FileWriter(dataFile)) { + for (Entry entry : map.entrySet()) { + writer.write(entry.getKey() + ";" + entry.getValue() + System.lineSeparator()); + } + } catch (IOException e) { + logger.warn("Failed writing file '{}': {}", dataFile.getName(), e.getMessage()); + } + } else { + logger.debug("System does not support bundle data files -> not persisting installed binding info"); + } + } + +} diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java index 88da4f09296..97604d42c06 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java @@ -7,34 +7,26 @@ */ package org.eclipse.smarthome.extensionservice.marketplace.internal; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.net.URL; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Map.Entry; +import java.util.Set; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.LineIterator; import org.apache.commons.lang.StringUtils; -import org.eclipse.smarthome.automation.parser.ParsingException; import org.eclipse.smarthome.core.events.Event; import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.extension.Extension; import org.eclipse.smarthome.core.extension.ExtensionEventFactory; import org.eclipse.smarthome.core.extension.ExtensionService; import org.eclipse.smarthome.core.extension.ExtensionType; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceExtension; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceExtensionHandler; +import org.eclipse.smarthome.extensionservice.marketplace.MarketplaceHandlerException; import org.eclipse.smarthome.extensionservice.marketplace.internal.model.Node; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,24 +39,14 @@ */ public class MarketplaceExtensionService implements ExtensionService { - private static final String BINDING_FILE = "installedBindingsMap.csv"; // constants used in marketplace nodes private static final String MP_PACKAGETYPE_BINDING = "binding"; private static final String MP_PACKAGETYPE_RULE_TEMPLATE = "rule_template"; - // constants used to construct extension IDs - private static final String EXT_PREFIX = "market:"; - private static final String EXT_TYPE_RULE_TEMPLATE = "ruletemplate"; - private static final String EXT_TYPE_BINDING = "binding"; - private final Logger logger = LoggerFactory.getLogger(MarketplaceExtensionService.class); - private List extensionTypes; private MarketplaceProxy proxy; private EventPublisher eventPublisher; - private BundleContext bundleContext; - private MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider; - private Map installedBindings; private Pattern labelPattern = Pattern.compile("<.*>"); // checks for the existence of any xml element private Pattern descriptionPattern = Pattern.compile("<(javascript|div|font)"); // checks for the existence of some // invalid elements @@ -72,18 +54,15 @@ public class MarketplaceExtensionService implements ExtensionService { private boolean includeBindings = true; private boolean includeRuleTemplates = true; private int maturityLevel = 1; + private Set extensionHandlers = new HashSet<>(); - protected void activate(BundleContext bundleContext, Map config) { - this.bundleContext = bundleContext; - installedBindings = loadInstalledBindingsMap(); + protected void activate(Map config) { this.proxy = new MarketplaceProxy(); modified(config); } protected void deactivate() { this.proxy = null; - this.installedBindings = null; - this.bundleContext = null; } protected void modified(Map config) { @@ -104,16 +83,6 @@ protected void modified(Map config) { "maturity"); } } - constructTypeList(); - } - - protected void setMarketplaceRuleTemplateProvider(MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider) { - this.marketplaceRuleTemplateProvider = marketplaceRuleTemplateProvider; - } - - protected void unsetMarketplaceRuleTemplateProvider( - MarketplaceRuleTemplateProvider marketplaceRuleTemplateProvider) { - this.marketplaceRuleTemplateProvider = null; } protected void setEventPublisher(EventPublisher eventPublisher) { @@ -124,6 +93,14 @@ protected void unsetEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = null; } + protected void addExtensionHandler(MarketplaceExtensionHandler handler) { + this.extensionHandlers.add(handler); + } + + protected void removeExtensionHandler(MarketplaceExtensionHandler handler) { + this.extensionHandlers.remove(handler); + } + @Override public List getExtensions(Locale locale) { List nodes = proxy.getNodes(); @@ -143,14 +120,26 @@ public List getExtensions(Locale locale) { continue; } - Extension ext = convertToExtension(node); + MarketplaceExtension ext = convertToExtension(node); if (ext != null) { - exts.add(ext); + if (setInstalledFlag(ext)) { + exts.add(ext); + } } } return exts; } + private boolean setInstalledFlag(MarketplaceExtension ext) { + for (MarketplaceExtensionHandler handler : extensionHandlers) { + if (handler.supports(ext)) { + ext.setInstalled(handler.isInstalled(ext)); + return true; + } + } + return false; + } + private MarketplaceExtension convertToExtension(Node node) { String extId = getExtensionId(node); @@ -162,13 +151,13 @@ private MarketplaceExtension convertToExtension(Node node) { return null; } if (MP_PACKAGETYPE_BINDING.equals(node.packagetypes)) { - MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_BINDING, name, node.version, - node.supporturl, isInstalled(extId), desc, null, node.image, node.updateurl); + MarketplaceExtension ext = new MarketplaceExtension(extId, MarketplaceExtension.EXT_TYPE_BINDING, name, + node.version, node.supporturl, false, desc, null, node.image, node.updateurl, node.packageformat); return ext; } else if (MP_PACKAGETYPE_RULE_TEMPLATE.equals(node.packagetypes)) { String version = StringUtils.isNotEmpty(node.version) ? node.version : "1.0"; - MarketplaceExtension ext = new MarketplaceExtension(extId, EXT_TYPE_RULE_TEMPLATE, name, version, - node.supporturl, isInstalled(extId), desc, null, node.image, node.updateurl); + MarketplaceExtension ext = new MarketplaceExtension(extId, MarketplaceExtension.EXT_TYPE_RULE_TEMPLATE, + name, version, node.supporturl, false, desc, null, node.image, node.updateurl, node.packageformat); return ext; } else { return null; @@ -187,123 +176,73 @@ public Extension getExtension(String id, Locale locale) { @Override public List getTypes(Locale locale) { - return extensionTypes; + ArrayList types = new ArrayList<>(2); + List exts = getExtensions(locale); + if (includeBindings) { + for (Extension ext : exts) { + if (ext.getType().equals(MarketplaceExtension.EXT_TYPE_BINDING)) { + types.add(new ExtensionType(MarketplaceExtension.EXT_TYPE_BINDING, "Bindings")); + break; + } + } + } + if (includeRuleTemplates) { + for (Extension ext : exts) { + if (ext.getType().equals(MarketplaceExtension.EXT_TYPE_RULE_TEMPLATE)) { + types.add(new ExtensionType(MarketplaceExtension.EXT_TYPE_RULE_TEMPLATE, "Rule Templates")); + break; + } + } + } + return Collections.unmodifiableList(types); } @Override public void install(String extensionId) { Extension ext = getExtension(extensionId, null); if (ext instanceof MarketplaceExtension) { - if (!ext.isInstalled()) { - MarketplaceExtension mpExt = (MarketplaceExtension) ext; - String type = getType(extensionId); - if (type != null) { - switch (type) { - case EXT_TYPE_RULE_TEMPLATE: - installRuleTemplate(extensionId, mpExt); - break; - case EXT_TYPE_BINDING: - installBinding(extensionId, mpExt); - break; - default: - postFailureEvent(extensionId, "Extension type " + type + " not known."); - break; + MarketplaceExtension mpExt = (MarketplaceExtension) ext; + for (MarketplaceExtensionHandler handler : extensionHandlers) { + if (handler.supports(mpExt)) { + if (!handler.isInstalled(mpExt)) { + try { + handler.install(mpExt); + postInstalledEvent(extensionId); + } catch (MarketplaceHandlerException e) { + postFailureEvent(extensionId, e.getMessage()); + } + } else { + postFailureEvent(extensionId, "Extension is already installed."); } - } else { - postFailureEvent(extensionId, "Extension not known."); + return; } - } else { - postFailureEvent(extensionId, "Extension is already installed."); } - } else { - postFailureEvent(extensionId, "Extension not known."); } + postFailureEvent(extensionId, "Extension not known."); } @Override public void uninstall(String extensionId) { Extension ext = getExtension(extensionId, null); - if (ext != null) { - if (ext.isInstalled()) { - String type = getType(extensionId); - if (type != null) { - switch (type) { - case EXT_TYPE_RULE_TEMPLATE: - marketplaceRuleTemplateProvider.remove(extensionId); + if (ext instanceof MarketplaceExtension) { + MarketplaceExtension mpExt = (MarketplaceExtension) ext; + for (MarketplaceExtensionHandler handler : extensionHandlers) { + if (handler.supports(mpExt)) { + if (handler.isInstalled(mpExt)) { + try { + handler.uninstall(mpExt); postUninstalledEvent(extensionId); - break; - case EXT_TYPE_BINDING: - uninstallBinding(extensionId); - break; - default: - postFailureEvent(extensionId, "Extension type " + type + " not known."); - break; + } catch (MarketplaceHandlerException e) { + postFailureEvent(extensionId, e.getMessage()); + } + } else { + postFailureEvent(extensionId, "Extension is not installed."); } - } else { - postFailureEvent(extensionId, "Extension not known."); - } - } else { - postFailureEvent(extensionId, "Extension is not installed."); - } - } else { - postFailureEvent(extensionId, "Extension not known."); - } - } - - private void installRuleTemplate(String extensionId, MarketplaceExtension mpExt) { - String url = mpExt.getDownloadUrl(); - try { - String template = getTemplate(url); - marketplaceRuleTemplateProvider.addTemplateAsJSON(extensionId, template); - postInstalledEvent(extensionId); - } catch (ParsingException e) { - postFailureEvent(extensionId, "Template is not valid."); - logger.error("Rule template from marketplace is invalid: {}", e.getMessage()); - } catch (IOException e) { - postFailureEvent(extensionId, "Template cannot be downloaded."); - logger.error("Rule template from marketplace cannot be downloaded: {}", e.getMessage()); - } - } - - private void installBinding(String extensionId, MarketplaceExtension mpExt) { - String url = mpExt.getDownloadUrl(); - try { - Bundle bundle = bundleContext.installBundle(url); - installedBindings.put(extensionId, bundle.getBundleId()); - persistInstalledBindingsMap(installedBindings); - postInstalledEvent(extensionId); - } catch (BundleException e) { - postFailureEvent(extensionId, "Binding cannot be installed: " + e.getMessage()); - } - } - - private void uninstallBinding(String extensionId) { - Long id = installedBindings.get(extensionId); - if (id != null) { - Bundle bundle = bundleContext.getBundle(id); - if (bundle != null) { - try { - bundle.uninstall(); - installedBindings.remove(extensionId); - persistInstalledBindingsMap(installedBindings); - postUninstalledEvent(extensionId); - } catch (BundleException e) { - postFailureEvent(extensionId, "Failed deinstalling binding: " + e.getMessage()); + return; } - } else { - // we do not have such a bundle, so let's remove it from our internal map - installedBindings.remove(extensionId); - persistInstalledBindingsMap(installedBindings); - postFailureEvent(extensionId, "Id not known."); } - } else { - postFailureEvent(extensionId, "Id not known."); } - } - - private String getTemplate(String urlString) throws IOException { - URL url = new URL(urlString); - return IOUtils.toString(url); + postFailureEvent(extensionId, "Extension not known."); } private void postInstalledEvent(String extensionId) { @@ -323,13 +262,13 @@ private void postFailureEvent(String extensionId, String msg) { } private String getExtensionId(Node node) { - StringBuilder sb = new StringBuilder(EXT_PREFIX); + StringBuilder sb = new StringBuilder(MarketplaceExtension.EXT_PREFIX); switch (node.packagetypes) { case MP_PACKAGETYPE_RULE_TEMPLATE: - sb.append(EXT_TYPE_RULE_TEMPLATE).append("-"); + sb.append(MarketplaceExtension.EXT_TYPE_RULE_TEMPLATE).append("-"); break; case MP_PACKAGETYPE_BINDING: - sb.append(EXT_TYPE_BINDING).append("-"); + sb.append(MarketplaceExtension.EXT_TYPE_BINDING).append("-"); break; default: return null; @@ -338,74 +277,6 @@ private String getExtensionId(Node node) { return sb.toString(); } - private boolean isInstalled(String extensionId) { - String type = getType(extensionId); - if (type == null) { - return false; - } - switch (type) { - case EXT_TYPE_RULE_TEMPLATE: - return marketplaceRuleTemplateProvider.get(extensionId) != null; - case EXT_TYPE_BINDING: - return installedBindings.containsKey(extensionId); - default: - return false; - } - } - - private String getType(String extensionId) { - if (extensionId.startsWith(EXT_PREFIX)) { - String idWithoutPrefix = extensionId.substring(EXT_PREFIX.length()); - return idWithoutPrefix.substring(0, idWithoutPrefix.lastIndexOf('-')); - } else { - return null; - } - } - - private Map loadInstalledBindingsMap() { - File dataFile = bundleContext.getDataFile(BINDING_FILE); - if (dataFile != null && dataFile.exists()) { - try (FileReader reader = new FileReader(dataFile)) { - LineIterator lineIterator = IOUtils.lineIterator(reader); - Map map = new HashMap<>(); - while (lineIterator.hasNext()) { - String line = lineIterator.nextLine(); - String[] parts = line.split(";"); - if (parts.length == 2) { - try { - map.put(parts[0], Long.valueOf(parts[1])); - } catch (NumberFormatException e) { - logger.debug("Cannot parse '{}' as a number in file {} - ignoring it.", parts[1], - dataFile.getName()); - } - } else { - logger.debug("Invalid line in file {} - ignoring it:\n{}", dataFile.getName(), line); - } - } - return map; - } catch (IOException e) { - logger.debug("File '{}' for installed bindings does not exist.", dataFile.getName()); - // ignore and just return an empty map - } - } - return new HashMap<>(); - } - - private synchronized void persistInstalledBindingsMap(Map map) { - File dataFile = bundleContext.getDataFile(BINDING_FILE); - if (dataFile != null) { - try (FileWriter writer = new FileWriter(dataFile)) { - for (Entry entry : map.entrySet()) { - writer.write(entry.getKey() + ";" + entry.getValue() + System.lineSeparator()); - } - } catch (IOException e) { - logger.warn("Failed writing file '{}': {}", dataFile.getName(), e.getMessage()); - } - } else { - logger.debug("System does not support bundle data files -> not persisting installed binding info"); - } - } - private int toMaturityLevel(String maturity) { switch (maturity) { case "Alpha": @@ -429,15 +300,4 @@ private boolean validName(String name) { private boolean validDescription(String desc) { return !descriptionPattern.matcher(desc).find(); } - - private void constructTypeList() { - ArrayList types = new ArrayList<>(2); - if (includeBindings) { - types.add(new ExtensionType(EXT_TYPE_BINDING, "Bindings")); - } - if (includeRuleTemplates) { - types.add(new ExtensionType(EXT_TYPE_RULE_TEMPLATE, "Rule Templates")); - } - extensionTypes = Collections.unmodifiableList(types); - } } From 05f8a03a793eca316faeef63757fcd639267d6c0 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Fri, 3 Mar 2017 15:41:52 +0100 Subject: [PATCH 05/10] added bundles to build and features Signed-off-by: Kai Kreuzer --- extensions/extensionservice/pom.xml | 23 +++++++++++++++++++ extensions/pom.xml | 1 + .../esh-ext/src/main/feature/feature.xml | 11 +++++++++ .../feature.xml | 13 +++++++++++ 4 files changed, 48 insertions(+) create mode 100644 extensions/extensionservice/pom.xml diff --git a/extensions/extensionservice/pom.xml b/extensions/extensionservice/pom.xml new file mode 100644 index 00000000000..5f694f6815b --- /dev/null +++ b/extensions/extensionservice/pom.xml @@ -0,0 +1,23 @@ + + + + + org.eclipse.smarthome.extension + pom + 0.9.0-SNAPSHOT + + + 4.0.0 + org.eclipse.smarthome.extensionservice + pom + + Eclipse SmartHome Extension Service Extensions + + pom + + + org.eclipse.smarthome.extensionservice.marketplace + org.eclipse.smarthome.extensionservice.marketplace.automation + + + diff --git a/extensions/pom.xml b/extensions/pom.xml index b11e901abac..9a40a9281ce 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -17,6 +17,7 @@ binding + extensionservice io transform ui diff --git a/features/karaf/esh-ext/src/main/feature/feature.xml b/features/karaf/esh-ext/src/main/feature/feature.xml index 663412bc619..1e9c95cd49e 100644 --- a/features/karaf/esh-ext/src/main/feature/feature.xml +++ b/features/karaf/esh-ext/src/main/feature/feature.xml @@ -55,6 +55,17 @@ mvn:org.eclipse.smarthome.binding/org.eclipse.smarthome.binding.yahooweather/${project.version} + + esh-base + mvn:org.eclipse.smarthome.extensionservice/org.eclipse.smarthome.extensionservice.marketplace/${project.version} + + + + esh-base + esh-automation-api + mvn:org.eclipse.smarthome.extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/${project.version} + + esh-base mvn:org.eclipse.smarthome.transform/org.eclipse.smarthome.transform.exec/${project.version} diff --git a/features/org.eclipse.smarthome.feature.runtime.core/feature.xml b/features/org.eclipse.smarthome.feature.runtime.core/feature.xml index e35abb1adad..74c5e82442a 100644 --- a/features/org.eclipse.smarthome.feature.runtime.core/feature.xml +++ b/features/org.eclipse.smarthome.feature.runtime.core/feature.xml @@ -271,4 +271,17 @@ version="0.0.0" unpack="false"/> + + + From 694beb108f4e30138d4f31a7b193dfb8b4203101 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Sun, 5 Mar 2017 10:08:35 +0100 Subject: [PATCH 06/10] added missing dependency to Karaf feature Signed-off-by: Kai Kreuzer --- features/karaf/esh-ext/src/main/feature/feature.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/features/karaf/esh-ext/src/main/feature/feature.xml b/features/karaf/esh-ext/src/main/feature/feature.xml index 1e9c95cd49e..1dd33a5e069 100644 --- a/features/karaf/esh-ext/src/main/feature/feature.xml +++ b/features/karaf/esh-ext/src/main/feature/feature.xml @@ -63,6 +63,7 @@ esh-base esh-automation-api + esh-extensionservice-marketplace mvn:org.eclipse.smarthome.extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/${project.version} From dbb298dc25b62b10e3abad08290a0e7468ea29b8 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Sun, 5 Mar 2017 19:13:25 +0100 Subject: [PATCH 07/10] addressed review comments Signed-off-by: Kai Kreuzer --- .../internal/MarketplaceRuleTemplateProvider.java | 7 ++----- .../internal/BindingExtensionHandler.java | 2 ++ .../internal/MarketplaceExtensionService.java | 2 +- .../marketplace/internal/MarketplaceProxy.java | 12 +++++++++++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java index b1a578a4245..0ddf6368462 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java @@ -23,11 +23,8 @@ import org.slf4j.LoggerFactory; /** - * This class provides local access to the market place content. Once started, it downloads the catalog and then makes - * its content available from memory. - * - * Note that there is no progressive/lazy browsing implemented yet, but the service downloads the whole catalog. - * Once the marketplace is filled with a lot of content, this will need to be addressed. + * This is a {@link RuleTemplateProvider}, which gets its content from the marketplace extension service + * and stores it through the ESH storage service. * * @author Kai Kreuzer - Initial contribution and API * diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java index f73e1cdd34e..724b8286ad7 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java @@ -73,6 +73,7 @@ public void install(MarketplaceExtension ext) throws MarketplaceHandlerException String url = ext.getDownloadUrl(); try { Bundle bundle = bundleContext.installBundle(url); + bundle.start(); installedBindings.put(ext.getId(), bundle.getBundleId()); persistInstalledBindingsMap(installedBindings); } catch (BundleException e) { @@ -87,6 +88,7 @@ public void uninstall(MarketplaceExtension ext) throws MarketplaceHandlerExcepti Bundle bundle = bundleContext.getBundle(id); if (bundle != null) { try { + bundle.stop(); bundle.uninstall(); installedBindings.remove(ext.getId()); persistInstalledBindingsMap(installedBindings); diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java index 97604d42c06..719f2e2c2f0 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java @@ -62,6 +62,7 @@ protected void activate(Map config) { } protected void deactivate() { + this.proxy.dispose(); this.proxy = null; } @@ -258,7 +259,6 @@ private void postUninstalledEvent(String extensionId) { private void postFailureEvent(String extensionId, String msg) { Event event = ExtensionEventFactory.createExtensionFailureEvent(extensionId, msg); eventPublisher.post(event); - } private String getExtensionId(Node node) { diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java index 144d57f45ae..8036b3746df 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java @@ -11,6 +11,8 @@ import java.net.URL; import java.util.Arrays; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.smarthome.config.xml.util.XmlDocumentReader; @@ -39,6 +41,7 @@ public class MarketplaceProxy { private Node[] cachedNodes = null; private long refresh_interval = 3600; private long retry_delay = 60; + private ScheduledFuture refreshJob; /** * Creates a new instance, which immediately schedules a synchronization with the marketplace content. @@ -46,7 +49,7 @@ public class MarketplaceProxy { public MarketplaceProxy() { try { url = new URL(MP_URL); - ThreadPoolManager.getScheduledPool("marketplace").scheduleWithFixedDelay(() -> refresh(), 0, + this.refreshJob = Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> refresh(), 0, refresh_interval, TimeUnit.SECONDS); } catch (MalformedURLException e) { throw new IllegalArgumentException("Something is very wrong - cannot instantiate URL " + MP_URL); @@ -86,4 +89,11 @@ public synchronized void refresh() { cachedNodes = null; getNodes(); } + + public void dispose() { + if (this.refreshJob != null && !this.refreshJob.isCancelled()) { + this.refreshJob.cancel(true); + this.refreshJob = null; + } + } } From 13bffde4b89cd99c1468e9cbc3c3abefd93151e8 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Sun, 5 Mar 2017 21:32:33 +0100 Subject: [PATCH 08/10] mark extension as installed, even if starting fails Signed-off-by: Kai Kreuzer --- .../marketplace/internal/BindingExtensionHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java index 724b8286ad7..41ca5d64c01 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/BindingExtensionHandler.java @@ -73,7 +73,11 @@ public void install(MarketplaceExtension ext) throws MarketplaceHandlerException String url = ext.getDownloadUrl(); try { Bundle bundle = bundleContext.installBundle(url); - bundle.start(); + try { + bundle.start(); + } catch (BundleException e) { + logger.warn("Installed bundle, but failed to start it: {}", e.getMessage()); + } installedBindings.put(ext.getId(), bundle.getBundleId()); persistInstalledBindingsMap(installedBindings); } catch (BundleException e) { From ede593cce63c7c87dde1f63e0421bc24b88594a6 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Sun, 5 Mar 2017 21:32:52 +0100 Subject: [PATCH 09/10] always set a version for bindings as well Signed-off-by: Kai Kreuzer --- .../marketplace/internal/MarketplaceExtensionService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java index 719f2e2c2f0..761741127f6 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceExtensionService.java @@ -146,6 +146,7 @@ private MarketplaceExtension convertToExtension(Node node) { String name = node.name; String desc = node.shortdescription; + String version = StringUtils.isNotEmpty(node.version) ? node.version : "1.0"; if (!validName(name) || !validDescription(desc)) { logger.debug("Ignoring node {} due to invalid content.", node.id); @@ -153,10 +154,9 @@ private MarketplaceExtension convertToExtension(Node node) { } if (MP_PACKAGETYPE_BINDING.equals(node.packagetypes)) { MarketplaceExtension ext = new MarketplaceExtension(extId, MarketplaceExtension.EXT_TYPE_BINDING, name, - node.version, node.supporturl, false, desc, null, node.image, node.updateurl, node.packageformat); + version, node.supporturl, false, desc, null, node.image, node.updateurl, node.packageformat); return ext; } else if (MP_PACKAGETYPE_RULE_TEMPLATE.equals(node.packagetypes)) { - String version = StringUtils.isNotEmpty(node.version) ? node.version : "1.0"; MarketplaceExtension ext = new MarketplaceExtension(extId, MarketplaceExtension.EXT_TYPE_RULE_TEMPLATE, name, version, node.supporturl, false, desc, null, node.image, node.updateurl, node.packageformat); return ext; From 344a982bc201b7233b49157f743199b385dc6460 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Sun, 5 Mar 2017 22:16:01 +0100 Subject: [PATCH 10/10] shutdown executor service on dispose Signed-off-by: Kai Kreuzer --- .../marketplace/internal/MarketplaceProxy.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java index 8036b3746df..0a8288d2580 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/internal/MarketplaceProxy.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -41,6 +42,7 @@ public class MarketplaceProxy { private Node[] cachedNodes = null; private long refresh_interval = 3600; private long retry_delay = 60; + private ScheduledExecutorService executorService; private ScheduledFuture refreshJob; /** @@ -49,8 +51,9 @@ public class MarketplaceProxy { public MarketplaceProxy() { try { url = new URL(MP_URL); - this.refreshJob = Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> refresh(), 0, - refresh_interval, TimeUnit.SECONDS); + this.executorService = Executors.newSingleThreadScheduledExecutor(); + this.refreshJob = this.executorService.scheduleWithFixedDelay(() -> refresh(), 0, refresh_interval, + TimeUnit.SECONDS); } catch (MalformedURLException e) { throw new IllegalArgumentException("Something is very wrong - cannot instantiate URL " + MP_URL); } @@ -95,5 +98,6 @@ public void dispose() { this.refreshJob.cancel(true); this.refreshJob = null; } + this.executorService.shutdown(); } }