<commit_before>/**
 * 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.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.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 <NAME> - 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 synchronized List<Node> getNodes() {
        if (cachedNodes == null) {
            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");
                    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();
    }

    public void dispose() {
        if (this.refreshJob != null && !this.refreshJob.isCancelled()) {
            this.refreshJob.cancel(true);
            this.refreshJob = null;
        }
        this.executorService.shutdown();
    }
}<commit_msg>fixed retry mechanism for marketplace proxy (#3151)<commit_after>/**
 * 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 <NAME> - 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();
    }
}
{'fraction_non_alphanumeric': 0.09176382098533283, 'fraction_numerical': 0.008273787138021812, 'mean_word_length': 2.9223205506391348, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2685376', 'n_tokens_mistral': 2110, 'n_tokens_neox': 2012, 'n_words': 600}