/** * 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.core.provider; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.eclipse.smarthome.automation.Rule; import org.eclipse.smarthome.automation.template.TemplateProvider; import org.eclipse.smarthome.automation.type.ModuleTypeProvider; import org.osgi.framework.Bundle; import org.osgi.framework.BundleEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is responsible for tracking the bundles providing automation resources and delegating the processing to * the responsible providers in separate thread. * * @author Ana Dimova - Initial Contribution, host-fragment support * @author Kai Kreuzer - refactored (managed) provider and registry implementation * @param <E> * */ public class AutomationResourceBundlesEventQueue<E> implements Runnable { /** * This field keeps instance of {@link Logger} that is used for logging. */ protected Logger logger = LoggerFactory.getLogger(this.getClass()); /** * This field serves for saving the BundleEvents for the bundles providing automation resources until their * processing completes. */ private List<BundleEvent> queue = new ArrayList<BundleEvent>(); /** * This field is for synchronization purposes */ private boolean running = false; private Thread runningThread; /** * This field is for synchronization purposes */ private boolean closed = false; /** * This field is for synchronization purposes */ private boolean shared = false; private AbstractResourceBundleProvider<E> provider; /** * This constructor is responsible for initializing a queue for bundles providing automation resources. * * @param provider * is a reference to an implementation of {@link TemplateProvider} or {@link ModuleTypeProvider} or an * importer of {@link Rule}s. */ public AutomationResourceBundlesEventQueue(AbstractResourceBundleProvider<E> provider) { this.provider = provider; } /** * When a new event for a bundle providing automation resources is received, this will causes a creation of a new * thread if there is no other created yet. If the thread already exists, then it will be notified for the event. * Starting the thread will cause the execution of this method in separate thread. * <p> * The general contract of this method <code>run</code> is invoking of the * {@link #processBundleChanged(BundleEvent)} method and executing it in separate thread. * * @see java.lang.Thread#run() */ @Override public void run() { boolean waitForEvents = true; while (true) { List<BundleEvent> l_queue = null; synchronized (this) { if (closed) { notifyAll(); return; } if (queue.isEmpty()) { if (waitForEvents) { try { wait(180000); } catch (Throwable t) { } waitForEvents = false; continue; } running = false; runningThread = null; notifyAll(); return; } l_queue = queue; shared = true; } Iterator<BundleEvent> events = l_queue.iterator(); while (events.hasNext()) { BundleEvent event = events.next(); try { processBundleChanged(event); } catch (Throwable t) { logger.warn("Processing bundle event {}, for automation resource bundle '{}' failed", event.getType(), event.getBundle().getSymbolicName(), t); } } synchronized (this) { if (shared) { queue.clear(); } shared = false; waitForEvents = true; notifyAll(); } } } /** * This method is invoked when this component is deactivated to stop the separate thread if still running. */ public void stop() { synchronized (this) { closed = true; notifyAll(); } Thread runningThread = this.runningThread; if (runningThread != null) { try { runningThread.join(30000); } catch (InterruptedException e) { } } } /** * This method is called when a new event for a bundle providing automation resources is received. It causes a * creation of a new thread if there is no other created yet and starting the it. If the thread already exists, * it is waiting for events and will be notified for the event. * * @param bundle providing automation resources * @param event for a bundle tracked by the {@code BundleTracker}. It has been for adding, modifying or removing the * bundle. */ protected synchronized void addEvent(Bundle bundle, BundleEvent event) { if (closed) { return; } if (shared) { queue = new LinkedList<BundleEvent>(); shared = false; } if (queue.add(event)) { logger.debug("Process bundle event {}, for automation bundle '{}' ", event.getType(), event.getBundle().getSymbolicName()); if (running) { notifyAll(); } else { runningThread = new Thread(this, "Automation Provider Processing Queue"); runningThread.start(); running = true; } } } /** * Depending on the action committed against the bundle supplier of automation resources, this method performs the * appropriate action - calls for it's host bundles: * <ul> * {@link AbstractResourceBundleProvider#processAutomationProviderUninstalled(Bundle)} method * </ul> * or * <ul> * {@link AbstractResourceBundleProvider#processAutomationProvider(Bundle)} method * </ul> * * @param event for a bundle tracked by the {@code BundleTracker}. It has been for adding, modifying or removing the * bundle. */ protected void processBundleChanged(BundleEvent event) { Bundle bundle = event.getBundle(); if (HostFragmentMappingUtil.isFragmentBundle(bundle)) { for (Bundle host : HostFragmentMappingUtil.returnHostBundles(bundle)) { provider.processAutomationProvider(host); } } else { switch (event.getType()) { case BundleEvent.UNINSTALLED: provider.processAutomationProviderUninstalled(bundle); break; default: provider.processAutomationProvider(bundle); } } } /** * This method is responsible for initializing the queue with all already received BundleEvents and starting a * thread that should process them. * * @param queue list with all already received BundleEvents */ protected synchronized void addAll(List<BundleEvent> queue) { if (closed) { return; } if (shared) { this.queue = new LinkedList<BundleEvent>(); shared = false; } if (this.queue.addAll(queue)) { if (running) { notifyAll(); } else { runningThread = new Thread(this, "Automation Provider Processing Queue"); runningThread.start(); running = true; } } } }