/**
* 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.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.smarthome.automation.ManagedRuleProvider;
import org.eclipse.smarthome.automation.Rule;
import org.eclipse.smarthome.automation.parser.Parser;
import org.osgi.framework.Bundle;
/**
* This class is implementation of {@link RuleResourceBundleImporter}. It serves for providing {@link Rule}s by loading
* bundle resources. It extends functionality of {@link AbstractResourceBundleProvider} by specifying:
* <ul>
* <li>the path to resources, corresponding to the {@link Rule}s - root directory
* {@link AbstractResourceBundleProvider#PATH} with sub-directory "rules".
* <li>type of the {@link Parser}s, corresponding to the {@link Rule}s - {@link Parser#PARSER_RULE}
* <li>specific functionality for loading the {@link Rule}s
* <li>tracking the managing service of the {@link Rule}s.
* </ul>
*
* @author Ana Dimova - Initial Contribution
* @author Kai Kreuzer - refactored (managed) provider and registry implementation
*
*/
public class RuleResourceBundleImporter extends AbstractResourceBundleProvider<Rule> {
/**
* This field holds the reference to the Rule Registry.
*/
protected ManagedRuleProvider mProvider;
/**
* This constructor is responsible for initializing the path to resources and tracking the managing service of the
* {@link Rule}s.
*
* @param registry the managing service of the {@link Rule}s.
*/
public RuleResourceBundleImporter() {
path = PATH + "/rules/";
}
protected void setManagedRuleProvider(ManagedRuleProvider mProvider) {
this.mProvider = mProvider;
}
@Override
public void deactivate() {
mProvider = null;
super.deactivate();
}
/**
* This method provides functionality for processing the bundles with rule resources.
* <p>
* Checks for availability of the needed {@link Parser} and for availability of the rules managing service. If one
* of them is not available - the bundle is added into {@link #waitingProviders} and the execution of the method
* ends.
* <p>
* Continues with loading the rules. If a rule already exists, it is updated, otherwise it is added.
* <p>
* The loading can fail because of {@link IOException}.
*
* @param bundle
* it is a {@link Bundle} which has to be processed, because it provides resources for automation rules.
*/
@Override
protected void processAutomationProvider(Bundle bundle) {
Vendor vendor = new Vendor(bundle.getSymbolicName(), bundle.getVersion().toString());
logger.debug("Parse rules from bundle '{}' ", bundle.getSymbolicName());
Enumeration<URL> urlEnum = null;
try {
if (bundle.getState() != Bundle.UNINSTALLED) {
urlEnum = bundle.findEntries(path, null, true);
}
} catch (IllegalStateException e) {
logger.debug("Can't read from resource of bundle with ID {}. The bundle is uninstalled.",
bundle.getBundleId(), e);
processAutomationProviderUninstalled(bundle);
}
if (urlEnum != null) {
while (urlEnum.hasMoreElements()) {
URL url = urlEnum.nextElement();
if (getPreviousPortfolio(vendor) != null
&& (waitingProviders.get(bundle) == null || !waitingProviders.get(bundle).contains(url))) {
return;
}
if (url.getPath().endsWith(File.separator)) {
continue;
}
String parserType = getParserType(url);
Parser<Rule> parser = parsers.get(parserType);
updateWaitingProviders(parser, bundle, url);
if (parser != null) {
Set<Rule> parsedObjects = parseData(parser, url, bundle);
if (parsedObjects != null && !parsedObjects.isEmpty()) {
Set<Rule> rules = setUIDs(vendor, parsedObjects);
addNewProvidedObjects(null, null, rules);
}
}
}
putNewPortfolio(vendor, Collections.<String> emptyList());
}
}
@Override
protected void addNewProvidedObjects(List<String> newPortfolio, List<String> previousPortfolio,
Set<Rule> parsedObjects) {
if (parsedObjects != null && !parsedObjects.isEmpty()) {
for (Rule rule : parsedObjects) {
try {
mProvider.add(rule);
} catch (IllegalArgumentException e) {
logger.debug("Not importing rule '{}' because: {}", rule.getUID(), e.getMessage(), e);
} catch (IllegalStateException e) {
logger.debug("Not importing rule '{}' since the rule registry is in an invalid state: {}",
rule.getUID(), e.getMessage());
}
}
}
}
@Override
protected List<String> getPreviousPortfolio(Vendor vendor) {
List<String> portfolio = providerPortfolio.get(vendor);
if (portfolio == null) {
for (Vendor v : providerPortfolio.keySet()) {
if (v.getVendorSymbolicName().equals(vendor.getVendorSymbolicName())) {
return providerPortfolio.get(v);
}
}
}
return portfolio;
}
@Override
protected void processAutomationProviderUninstalled(Bundle bundle) {
Vendor vendor = new Vendor(bundle.getSymbolicName(), bundle.getVersion().toString());
waitingProviders.remove(bundle);
providerPortfolio.remove(vendor);
}
@Override
protected boolean checkExistence(String uid) {
return mProvider.get(uid) != null;
}
@Override
protected String getUID(Rule parsedObject) {
return parsedObject.getUID();
}
protected Set<Rule> setUIDs(Vendor vendor, Set<Rule> rules) {
Set<Rule> newRules = new HashSet<Rule>();
for (Rule rule : rules) {
if (rule.getUID() == null) {
rule = setUID(vendor, rule);
}
newRules.add(rule);
}
return newRules;
}
/**
* This method gives UIDs on the rules that don't have one.
*
* @param vendor
* is the bundle providing the rules.
* @param rule
* is the provided rule.
*/
private Rule setUID(Vendor vendor, Rule rule) {
String uid = vendor.getVendorID() + vendor.count();
Rule r = new Rule(uid, rule.getTriggers(), rule.getConditions(), rule.getActions(),
rule.getConfigurationDescriptions(), rule.getConfiguration(), rule.getTemplateUID(),
rule.getVisibility());
r.setName(rule.getName());
r.setDescription(rule.getDescription());
r.setTags(rule.getTags());
return r;
}
}