/** * 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.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.smarthome.config.xml.util.XmlDocumentReader; 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; private ScheduledExecutorService executorService; private ScheduledFuture<?> refreshJob; /** * Creates a new instance, which immediately schedules a synchronization with the marketplace content. */ public MarketplaceProxy() { try { url = new URL(MP_URL); 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); } } /** * returns the full list of marketplace nodes * * @return list of marketplace nodes */ public List<Node> getNodes() { return cachedNodes != null ? Arrays.asList(cachedNodes) : Collections.emptyList(); } /** * Refreshes the local content by synchronizing with the remote marketplace. */ public synchronized void refresh() { XmlDocumentReader<Marketplace> 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"); this.executorService.schedule(() -> refresh(), retry_delay, TimeUnit.SECONDS); } else { logger.debug("Cannot access IoT Marketplace - will continue to use cached results: {}", e.getMessage()); } } } public void dispose() { if (this.refreshJob != null && !this.refreshJob.isCancelled()) { this.refreshJob.cancel(true); this.refreshJob = null; } this.executorService.shutdown(); } }