Skip to content

Commit

Permalink
[automation] Added optional per-script StartLevels
Browse files Browse the repository at this point in the history
Allows scripts to specify per-script start levels, which differ from the start level for the ScriptFileWatcher itself. Also extracted some functionality from ScriptFileWatcher into distinct components & added unit tests to cover all script loading functionality.

Signed-off-by: Jonathan Gilbert <[email protected]>
  • Loading branch information
jpg0 committed Mar 4, 2021
1 parent 62c74da commit 2deb01e
Show file tree
Hide file tree
Showing 6 changed files with 754 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.openhab.core.automation.module.script.rulesupport.internal.loader.collection.BidiSetBag;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -24,10 +28,13 @@
*
* @author Jonathan Gilbert
*/
public abstract class DependencyTracker {
@Component(immediate = true, service = DependencyTracker.class)
public class DependencyTracker {

private final Logger logger = LoggerFactory.getLogger(DependencyTracker.class);

private final Set<DependencyChangeListener> dependencyChangeListeners = ConcurrentHashMap.newKeySet();

private final BidiSetBag<String, String> scriptToLibs = new BidiSetBag<>();
private final ScriptLibraryWatcher scriptLibraryWatcher = new ScriptLibraryWatcher() {
@Override
Expand All @@ -45,16 +52,16 @@ void updateFile(String libraryPath) {
}
};

@Activate
public void activate() {
scriptLibraryWatcher.activate();
}

@Deactivate
public void deactivate() {
scriptLibraryWatcher.deactivate();
}

public abstract void reimportScript(String scriptPath);

public void addLibForScript(String scriptPath, String libPath) {
synchronized (scriptToLibs) {
scriptToLibs.put(scriptPath, libPath);
Expand All @@ -66,4 +73,26 @@ public void removeScript(String scriptPath) {
scriptToLibs.removeKey(scriptPath);
}
}

public void addChangeTracker(DependencyChangeListener listener) {
dependencyChangeListeners.add(listener);
}

public void removeChangeTracker(DependencyChangeListener listener) {
dependencyChangeListeners.remove(listener);
}

public void reimportScript(String scriptPath) {
for (DependencyChangeListener listener : dependencyChangeListeners) {
try {
listener.onDependencyChange(scriptPath);
} catch (Exception e) {
logger.warn("Failed to notify tracker of dependency change: {}: {}", e.getClass(), e.getMessage());
}
}
}

public interface DependencyChangeListener {
void onDependencyChange(String scriptPath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.automation.module.script.rulesupport.internal.loader;

import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.jetbrains.annotations.NotNull;
import org.openhab.core.service.StartLevelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Script File wrapper offering various methods to inspect the script
*
* @author Jonathan Gilbert - initial contribution
*/
@NonNullByDefault
public class ScriptFileReference implements Comparable<ScriptFileReference> {

private static final Set<String> EXCLUDED_FILE_EXTENSIONS = new HashSet<>(
Arrays.asList("txt", "old", "example", "backup", "md", "swp", "tmp", "bak"));

private static final Pattern[] startLevelPatterns = new Pattern[] { Pattern.compile(".*/sl(\\d{2})/[^/]+"), // script
// in
// immediate
// slXX
// directory
Pattern.compile(".*/[^/]+\\.sl(\\d{2})\\.[^/.]+") // script named <name>.slXX.<ext>
};

private static final Logger logger = LoggerFactory.getLogger(ScriptFileReference.class);

private final URL scriptFileURL;

public ScriptFileReference(URL scriptFileURL) {
this.scriptFileURL = scriptFileURL;
}

public URL getScriptFileURL() {
return scriptFileURL;
}

public int getStartLevel() {
for (Pattern p : startLevelPatterns) {
Matcher m = p.matcher(scriptFileURL.getPath());
if (m.find() && m.groupCount() > 0) {
try {
return Integer.parseInt(m.group(1));
} catch (NumberFormatException nfe) {
logger.warn("Extracted start level {} from {}, but it's not an integer. Ignoring.", m.group(1),
scriptFileURL.getPath());
}
}
}

return StartLevelService.STARTLEVEL_RULEENGINE;
}

public Optional<String> getScriptType() {
String fileName = scriptFileURL.getPath();
int index = fileName.lastIndexOf(".");
if (index == -1) {
return Optional.empty();
}
String fileExtension = fileName.substring(index + 1);

// ignore known file extensions for "temp" files
if (EXCLUDED_FILE_EXTENSIONS.contains(fileExtension) || fileExtension.endsWith("~")) {
return Optional.empty();
}
return Optional.of(fileExtension);
}

public String getScriptIdentifier() {
return scriptFileURL.toString();
}

@Override
public int compareTo(@NotNull ScriptFileReference other) {
try {
Path path1 = Paths.get(scriptFileURL.toURI());
String name1 = path1.getFileName().toString();
logger.trace("o1 [{}], path1 [{}], name1 [{}]", scriptFileURL, path1, name1);

Path path2 = Paths.get(other.scriptFileURL.toURI());
String name2 = path2.getFileName().toString();
logger.trace("o2 [{}], path2 [{}], name2 [{}]", other.scriptFileURL, path2, name2);

int nameCompare = name1.compareToIgnoreCase(name2);
if (nameCompare != 0) {
return nameCompare;
} else {
return path1.getParent().toString().compareToIgnoreCase(path2.getParent().toString());
}
} catch (URISyntaxException e) {
logger.error("URI syntax exception", e);
return 0;
}
}
}
Loading

0 comments on commit 2deb01e

Please sign in to comment.