Add a ScriptEngineFactory bundle tracker (#3275)

* Add a ScriptEngineFactory bundle tracker

The `ScriptEngingeFactoryBundleTracker` tracks all bundles that provide `ScriptEngineFcatory` capabilities and sets a `ReadyMarker` if all of them are activated.

Signed-off-by: Jan N. Klug <github@klug.nrw>
pull/3637/head
J-N-K 2023-05-27 21:48:16 +02:00 committed by GitHub
parent 6bb15b806c
commit 796f66a2fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 157 additions and 1 deletions

View File

@ -0,0 +1,156 @@
/**
* Copyright (c) 2010-2022 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.internal;
import java.util.Dictionary;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.openhab.core.service.ReadyMarker;
import org.openhab.core.service.ReadyMarkerFilter;
import org.openhab.core.service.ReadyService;
import org.openhab.core.service.StartLevelService;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.util.tracker.BundleTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ScriptEngineFactoryBundleTracker} tracks bundles that provide {@link ScriptEngineFactory} and sets the
* {@link #READY_MARKER} when all registered bundles are active
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
@Component(immediate = true)
public class ScriptEngineFactoryBundleTracker extends BundleTracker<Bundle> implements ReadyService.ReadyTracker {
private static final int STATE_MASK = Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE | Bundle.STARTING
| Bundle.STOPPING | Bundle.UNINSTALLED;
public static final ReadyMarker READY_MARKER = new ReadyMarker("automation", "scriptEngineFactories");
private final Logger logger = LoggerFactory.getLogger(ScriptEngineFactoryBundleTracker.class);
private final ReadyService readyService;
private final StartLevelService startLevelService;
private final Map<String, Integer> bundles = new ConcurrentHashMap<>();
private boolean ready = false;
@Activate
public ScriptEngineFactoryBundleTracker(final @Reference ReadyService readyService,
final @Reference StartLevelService startLevelService, BundleContext bc) {
super(bc, STATE_MASK, null);
this.readyService = readyService;
this.startLevelService = startLevelService;
this.open();
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE)
.withIdentifier(Integer.toString(StartLevelService.STARTLEVEL_OSGI)));
}
@Deactivate
public void deactivate() throws Exception {
this.close();
ready = false;
}
private boolean allBundlesActive() {
return bundles.values().stream().allMatch(i -> i == Bundle.ACTIVE);
}
@Override
public Bundle addingBundle(@NonNullByDefault({}) Bundle bundle, @Nullable BundleEvent event) {
String bsn = bundle.getSymbolicName();
int state = bundle.getState();
if (isScriptingBundle(bundle)) {
logger.debug("Added {}: {} ", bsn, stateToString(state));
bundles.put(bsn, state);
}
checkReady();
return bundle;
}
@Override
public void modifiedBundle(@NonNullByDefault({}) Bundle bundle, @Nullable BundleEvent event,
@NonNullByDefault({}) Bundle object) {
String bsn = bundle.getSymbolicName();
int state = bundle.getState();
if (isScriptingBundle(bundle)) {
logger.debug("Modified {}: {}", bsn, stateToString(state));
bundles.put(bsn, state);
}
checkReady();
}
@Override
public void removedBundle(@NonNullByDefault({}) Bundle bundle, @Nullable BundleEvent event,
@NonNullByDefault({}) Bundle object) {
String bsn = bundle.getSymbolicName();
if (isScriptingBundle(bundle)) {
logger.debug("Removed {}", bsn);
bundles.remove(bsn);
}
checkReady();
}
@Override
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
checkReady();
}
@Override
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
ready = false;
readyService.unmarkReady(READY_MARKER);
}
private void checkReady() {
if (!ready && startLevelService.getStartLevel() > StartLevelService.STARTLEVEL_OSGI && allBundlesActive()) {
logger.info("All automation bundles ready.");
readyService.markReady(READY_MARKER);
ready = true;
} else if (ready && !allBundlesActive()) {
readyService.unmarkReady(READY_MARKER);
ready = false;
}
}
private String stateToString(int state) {
return switch (state) {
case Bundle.UNINSTALLED -> "UNINSTALLED";
case Bundle.INSTALLED -> "INSTALLED";
case Bundle.RESOLVED -> "RESOLVED";
case Bundle.STARTING -> "STARTING";
case Bundle.STOPPING -> "STOPPING";
case Bundle.ACTIVE -> "ACTIVE";
default -> "UNKNOWN";
};
}
private boolean isScriptingBundle(Bundle bundle) {
Dictionary<String, String> headers = bundle.getHeaders();
String provideCapability = headers.get("Provide-Capability");
return provideCapability != null && provideCapability.contains(ScriptEngineFactory.class.getName());
}
}

View File

@ -58,7 +58,7 @@ import org.slf4j.LoggerFactory;
* 10 - OSGi application start level has been reached, i.e. bundles are activated.
* 20 - Model entities (items, things, links, persist config) have been loaded, both from db as well as files.
* 30 - Item states have been restored from persistence service, where applicable.
* 40 - Rules are loaded and parsed, both from db as well as dsl and script files.
* 40 - Rules from db, dsl and script files are loaded and parsed, script engine factories are available.
* 50 - Rule engine has executed all "system started" rules and is active.
* 70 - User interface is up and running.
* 80 - All things have been initialized.