/** * Copyright (c) 1997, 2015 by ProSyst Software GmbH and others. * 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.automation.internal.commands; import java.io.File; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.smarthome.automation.parser.Parser; import org.eclipse.smarthome.automation.parser.ParsingException; import org.eclipse.smarthome.automation.template.TemplateProvider; import org.eclipse.smarthome.automation.type.ModuleTypeProvider; import org.eclipse.smarthome.core.common.registry.ProviderChangeListener; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is base for {@link ModuleTypeProvider}, {@link TemplateProvider} and RuleImporter which are responsible * for execution of automation commands. * <p> * It provides functionality for tracking {@link Parser} services by implementing {@link ServiceTrackerCustomizer} and * provides common functionality for exporting automation objects. * * @author Ana Dimova - Initial Contribution * @author Kai Kreuzer - refactored (managed) provider and registry implementation * */ @SuppressWarnings("rawtypes") public abstract class AbstractCommandProvider<E> implements ServiceTrackerCustomizer { protected Logger logger; /** * A bundle's execution context within the Framework. */ protected BundleContext bc; /** * This Map provides reference between provider of resources and the loaded objects from these resources. * <p> * The Map has for keys - {@link URL} resource provider and for values - Lists with UIDs of the objects. */ Map<URL, List<String>> providerPortfolio = new HashMap<URL, List<String>>(); /** * This field is a {@link ServiceTracker} for {@link Parser} services. */ protected ServiceTracker<Parser, Parser> parserTracker; /** * This Map provides structure for fast access to the {@link Parser}s. This provides opportunity for high * performance at runtime of the system. */ protected Map<String, Parser<E>> parsers = new HashMap<String, Parser<E>>(); /** * This Map provides structure for fast access to the provided automation objects. This provides opportunity for * high performance at runtime of the system, when the Rule Engine asks for any particular object, instead of * waiting it for parsing every time. * <p> * The Map has for keys UIDs of the objects and for values {@link Localizer}s of the objects. */ protected Map<String, E> providedObjectsHolder = new HashMap<String, E>(); protected List<ProviderChangeListener<E>> listeners; /** * This constructor is responsible for creation and opening a tracker for {@link Parser} services. * * @param context is the {@link BundleContext}, used for creating a tracker for {@link Parser} services. */ @SuppressWarnings("unchecked") public AbstractCommandProvider(BundleContext context) { this.bc = context; logger = LoggerFactory.getLogger(AbstractCommandProvider.this.getClass()); parserTracker = new ServiceTracker(context, Parser.class.getName(), this); parserTracker.open(); } /** * This method is inherited from {@link AbstractPersistentProvider}. * Extends parent's functionality with closing the {@link Parser} service tracker. * Sets <code>null</code> to {@link #parsers}, {@link #providedObjectsHolder}, {@link #providerPortfolio} */ public void close() { if (parserTracker != null) { parserTracker.close(); parserTracker = null; parsers = null; synchronized (providedObjectsHolder) { providedObjectsHolder = null; } synchronized (providerPortfolio) { providerPortfolio = null; } } } /** * This method tracks the {@link Parser} services and stores them into the Map "{@link #parsers}" in the * memory, for fast access on demand. * * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi.framework.ServiceReference) */ @Override public Object addingService(ServiceReference reference) { @SuppressWarnings("unchecked") Parser<E> service = (Parser<E>) bc.getService(reference); String key = (String) reference.getProperty(Parser.FORMAT); key = key == null ? Parser.FORMAT_JSON : key; parsers.put(key, service); return service; } /** * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, * java.lang.Object) */ @Override public void modifiedService(ServiceReference reference, Object service) { // do nothing } /** * This method removes the {@link Parser} service objects from the Map "{@link #parsers}". * * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi.framework.ServiceReference, * java.lang.Object) */ @Override public void removedService(ServiceReference reference, Object service) { String key = (String) reference.getProperty(Parser.FORMAT); key = key == null ? Parser.FORMAT_JSON : key; parsers.remove(key); } /** * This method is responsible for execution of the {@link AutomationCommandExport} operation by choosing the * {@link Parser} which to be used for exporting a set of automation objects, in a file. When the choice is made, * the chosen {@link Parser} is used to do the export. * * @param parserType is a criteria for choosing the {@link Parser} which to be used. * @param set a Set of automation objects for export. * @param file is the file in which to export the automation objects. * @throws Exception is thrown when I/O operation has failed or has been interrupted or generating of the text fails * for some reasons. */ public String exportData(String parserType, Set<E> set, File file) throws Exception { Parser<E> parser = parsers.get(parserType); if (parser != null) { try (OutputStreamWriter oWriter = new OutputStreamWriter(new FileOutputStream(file))) { parser.serialize(set, oWriter); return AutomationCommand.SUCCESS; } } else { return String.format("%s! Parser \"%s\" not found!", AutomationCommand.FAIL, parserType); } } /** * This method is responsible for execution of the {@link AutomationCommandImport} operation. * * @param parser the {@link Parser} which to be used for operation. * @param inputStreamReader * @return the set of automation objects created as result of the {@link AutomationCommandImport} operation. * Operation can be successful or can fail because of {@link ParsingException}. * @throws ParsingException is thrown when there are exceptions during the parsing process. It accumulates all of * them. */ protected abstract Set<E> importData(URL url, Parser<E> parser, InputStreamReader inputStreamReader) throws ParsingException; }