|
|
|
@ -2,7 +2,7 @@
|
|
|
|
|
* ~~~~~~licensing~~~~~~
|
|
|
|
|
* init-manager
|
|
|
|
|
* ==========
|
|
|
|
|
* Copyright (C) 2020 - 2023 EmDev LLC
|
|
|
|
|
* Copyright (C) 2020 - 2024 EmDev LLC
|
|
|
|
|
* ==========
|
|
|
|
|
* You may not use this file except in accordance with the License Terms of the Copyright
|
|
|
|
|
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
|
|
|
@ -29,7 +29,9 @@ import java.io.IOException;
|
|
|
|
|
import java.lang.reflect.Constructor;
|
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Dictionary;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Hashtable;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Set;
|
|
|
|
@ -40,13 +42,14 @@ import org.apache.felix.utils.properties.TypedProperties;
|
|
|
|
|
import org.apache.karaf.config.core.ConfigRepository;
|
|
|
|
|
import org.osgi.framework.Bundle;
|
|
|
|
|
import org.osgi.framework.BundleContext;
|
|
|
|
|
import org.osgi.framework.FrameworkUtil;
|
|
|
|
|
import org.osgi.framework.InvalidSyntaxException;
|
|
|
|
|
import org.osgi.framework.ServiceReference;
|
|
|
|
|
import org.osgi.framework.ServiceRegistration;
|
|
|
|
|
import org.osgi.framework.wiring.BundleWiring;
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
|
|
import ru.entaxy.esb.platform.runtime.core.initializer.api.Initialized;
|
|
|
|
|
import ru.entaxy.esb.platform.runtime.core.initializer.api.Initializer;
|
|
|
|
|
import ru.entaxy.esb.platform.runtime.core.initializer.api.InitializerException;
|
|
|
|
|
|
|
|
|
@ -54,390 +57,436 @@ import ru.entaxy.esb.platform.runtime.core.initializer.api.InitializerException;
|
|
|
|
|
*
|
|
|
|
|
* Manager for initializers
|
|
|
|
|
*
|
|
|
|
|
* TODO (if needed):
|
|
|
|
|
* 1. keep map "initializerClassName -> bundleId"
|
|
|
|
|
* 2. publish as a service
|
|
|
|
|
* 3. provide interface with method "reinit(String className)"
|
|
|
|
|
* TODO (if needed): 1. keep map "initializerClassName -> bundleId" 2. publish as a service 3.
|
|
|
|
|
* provide interface with method "reinit(String className)"
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @author sstarovoytenkov
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public class InitManager {
|
|
|
|
|
|
|
|
|
|
protected static final Logger log = LoggerFactory.getLogger(InitManager.class);
|
|
|
|
|
|
|
|
|
|
public static final String PROP_SKIP_ALL = "skip.all";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* pid of config to store initialization status (true/false)
|
|
|
|
|
* will be created on first start of InitManager
|
|
|
|
|
*/
|
|
|
|
|
protected final String pid = "ru.entaxy.esb.initializer";
|
|
|
|
|
|
|
|
|
|
protected ConfigRepository configRepository;
|
|
|
|
|
protected BundleContext bundleContext;
|
|
|
|
|
|
|
|
|
|
// used when updating to avoid multiple extra initializations
|
|
|
|
|
protected boolean skipAll = false;
|
|
|
|
|
|
|
|
|
|
protected Map<String, Set<String>> initersToDeps = new HashMap<>();
|
|
|
|
|
protected Map<String, Set<String>> depsToIniters = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
protected Map<String, InitializerMeta> initializers = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
protected List<String> waiting = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
protected class InitializerMeta implements Initializer.Callback {
|
|
|
|
|
|
|
|
|
|
String id;
|
|
|
|
|
String className;
|
|
|
|
|
boolean repeat;
|
|
|
|
|
|
|
|
|
|
// Bundle bundle;
|
|
|
|
|
long bundleId;
|
|
|
|
|
|
|
|
|
|
BundleContext bundleContext;
|
|
|
|
|
|
|
|
|
|
Map<String, InitializerMeta> dependsOn = new HashMap<>();
|
|
|
|
|
Map<String, InitializerMeta> dependedBy = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
boolean executed = false;
|
|
|
|
|
boolean toBeExecuted = true;
|
|
|
|
|
|
|
|
|
|
int retries = 1;
|
|
|
|
|
|
|
|
|
|
int interval = 1000;
|
|
|
|
|
|
|
|
|
|
public void load(String initializerData) {
|
|
|
|
|
String[] data = initializerData.split("\\?");
|
|
|
|
|
|
|
|
|
|
this.className = data[0];
|
|
|
|
|
String queryString = data.length>1?data[1]:"";
|
|
|
|
|
|
|
|
|
|
String[] queryStringData = queryString.split("&");
|
|
|
|
|
Map<String, String> params = new HashMap<>();
|
|
|
|
|
for (int i=0; i<queryStringData.length; i++) {
|
|
|
|
|
String[] paramData = queryStringData[i].split("=");
|
|
|
|
|
params.put(paramData[0], paramData.length>1?paramData[1]:"");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.id = params.containsKey(Initializer.INITIALIZER_QUERY_STRING_PARAM_ID)
|
|
|
|
|
?(Strings.isNullOrEmpty(params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_ID))
|
|
|
|
|
?className
|
|
|
|
|
:params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_ID))
|
|
|
|
|
:className;
|
|
|
|
|
|
|
|
|
|
String dependsOn = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_DEPENDS_ON);
|
|
|
|
|
if (!Strings.isNullOrEmpty(dependsOn)) {
|
|
|
|
|
String[] dependencies = dependsOn.split(",");
|
|
|
|
|
for (int i=0; i<dependencies.length; i++) {
|
|
|
|
|
InitializerMeta meta = InitManager.this.getMetaById(dependencies[i]);
|
|
|
|
|
addDependency(meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
String repeat = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_REPEAT);
|
|
|
|
|
this.repeat = "true".equals(repeat);
|
|
|
|
|
|
|
|
|
|
String retriesValue = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_RETRIES);
|
|
|
|
|
if (!Strings.isNullOrEmpty(retriesValue))
|
|
|
|
|
try {
|
|
|
|
|
this.retries = Integer.parseInt(retriesValue);
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("Retries parameter [{}] is not an integer", retriesValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String intervalValue = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_INTERVAL);
|
|
|
|
|
if (!Strings.isNullOrEmpty(intervalValue))
|
|
|
|
|
try {
|
|
|
|
|
this.interval = Integer.parseInt(intervalValue);
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("Interval parameter [{}] is not an integer", intervalValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void addDependency(InitializerMeta meta) {
|
|
|
|
|
synchronized (meta) {
|
|
|
|
|
if (meta.toBeExecuted && !meta.executed)
|
|
|
|
|
synchronized (this.dependsOn) {
|
|
|
|
|
meta.addDependent(this);
|
|
|
|
|
this.dependsOn.put(meta.id, meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void load(InitializerMeta other) {
|
|
|
|
|
this.className = other.className;
|
|
|
|
|
this.bundleId = other.bundleId;
|
|
|
|
|
this.repeat = other.repeat;
|
|
|
|
|
this.retries = other.retries;
|
|
|
|
|
this.interval = other.interval;
|
|
|
|
|
this.bundleContext = other.bundleContext;
|
|
|
|
|
for (InitializerMeta meta: other.dependsOn.values())
|
|
|
|
|
this.addDependency(meta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void addDependent(InitializerMeta meta) {
|
|
|
|
|
synchronized (this.dependedBy) {
|
|
|
|
|
this.dependedBy.put(meta.id, meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
protected static final Logger log = LoggerFactory.getLogger(InitManager.class);
|
|
|
|
|
|
|
|
|
|
public boolean isExecutedBefore() {
|
|
|
|
|
return InitManager.this.getValueById(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean canExecute() {
|
|
|
|
|
return this.dependsOn.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void notifyDependent() {
|
|
|
|
|
for (InitializerMeta meta: this.dependedBy.values())
|
|
|
|
|
meta.dependencyReady(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Bundle getBundle() {
|
|
|
|
|
return bundleContext.getBundle(bundleId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean execute() {
|
|
|
|
|
/**
|
|
|
|
|
* instantiate initializer and call "init"
|
|
|
|
|
* if no exceptions, set status to "true"
|
|
|
|
|
*
|
|
|
|
|
* @see <code>Initializer</code>
|
|
|
|
|
*/
|
|
|
|
|
Bundle bundle = getBundle();
|
|
|
|
|
BundleWiring wiring = bundle.adapt(BundleWiring.class);
|
|
|
|
|
ClassLoader cl = wiring.getClassLoader();
|
|
|
|
|
Class<?> clazz;
|
|
|
|
|
try {
|
|
|
|
|
if (!InitManager.this.skipAll) {
|
|
|
|
|
clazz = cl.loadClass(className);
|
|
|
|
|
log.info("Loaded class {}", clazz.getName());
|
|
|
|
|
Constructor<?> constructor = clazz.getConstructor();
|
|
|
|
|
Initializer initializer = (Initializer) constructor.newInstance();
|
|
|
|
|
initializer.setBundleContext(bundle.getBundleContext());
|
|
|
|
|
initializer.setInitializerId(id);
|
|
|
|
|
initializer.init();
|
|
|
|
|
}
|
|
|
|
|
InitManager.this.updateById(id, true);
|
|
|
|
|
this.executed = true;
|
|
|
|
|
this.notifyDependent();
|
|
|
|
|
return true;
|
|
|
|
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InitializerException e) {
|
|
|
|
|
log.error("Initializer error: class=" + className
|
|
|
|
|
+ ", id=" + id + " -> [" + e.getMessage() + "] :: "
|
|
|
|
|
+ (e instanceof InitializerException?((InitializerException)e).getDescription():""), e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
public static final String PROP_SKIP_ALL = "skip.all";
|
|
|
|
|
|
|
|
|
|
public void runInitializer() {
|
|
|
|
|
Thread thread = new Thread(new Executor(this));
|
|
|
|
|
thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
|
|
|
|
|
thread.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setCallback() {
|
|
|
|
|
BundleWiring wiring = getBundle().adapt(BundleWiring.class);
|
|
|
|
|
ClassLoader cl = wiring.getClassLoader();
|
|
|
|
|
Class<?> clazz;
|
|
|
|
|
try {
|
|
|
|
|
clazz = cl.loadClass(className);
|
|
|
|
|
log.info("Loaded class {}", clazz.getName());
|
|
|
|
|
Constructor<?> constructor = clazz.getConstructor();
|
|
|
|
|
Initializer initializer = (Initializer) constructor.newInstance();
|
|
|
|
|
initializer.setCallback(this);
|
|
|
|
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
|
|
|
|
// TODO Auto-generated catch block
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void dependencyReady(InitializerMeta meta) {
|
|
|
|
|
synchronized (this.dependsOn) {
|
|
|
|
|
this.dependsOn.remove(meta.id);
|
|
|
|
|
if (!this.executed && this.canExecute())
|
|
|
|
|
// this.execute();
|
|
|
|
|
this.runInitializer();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void inited(Initializer owner) {
|
|
|
|
|
log.info("Initializer with id {} notified of successful init via callback", id);
|
|
|
|
|
InitManager.this.updateById(id, true);
|
|
|
|
|
this.executed = true;
|
|
|
|
|
this.notifyDependent();
|
|
|
|
|
owner.setCallback(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected static class Executor implements Runnable {
|
|
|
|
|
|
|
|
|
|
InitializerMeta meta;
|
|
|
|
|
|
|
|
|
|
public Executor(InitializerMeta meta) {
|
|
|
|
|
this.meta = meta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
/**
|
|
|
|
|
* pid of config to store initialization status (true/false) will be created on first start of
|
|
|
|
|
* InitManager
|
|
|
|
|
*/
|
|
|
|
|
protected final String pid = "ru.entaxy.esb.initializer";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* check the initialization status
|
|
|
|
|
*/
|
|
|
|
|
if (meta.isExecutedBefore()) {
|
|
|
|
|
log.info("Initializer with id {} in bundle [{}] {} is already inited", meta.id,
|
|
|
|
|
meta.bundleId, meta.getBundle().getSymbolicName());
|
|
|
|
|
protected ConfigRepository configRepository;
|
|
|
|
|
protected BundleContext bundleContext;
|
|
|
|
|
|
|
|
|
|
if (meta.repeat)
|
|
|
|
|
log.info("Initializer with id {} is set to be repeated", meta.id);
|
|
|
|
|
else {
|
|
|
|
|
meta.toBeExecuted = false;
|
|
|
|
|
meta.notifyDependent();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!meta.canExecute()) {
|
|
|
|
|
String dependsOn = meta.dependsOn.values().stream().map((m)->m.id).collect(Collectors.joining(","));
|
|
|
|
|
log.info("Initializer with id {} is waiting for dependencies: {}", meta.id, dependsOn);
|
|
|
|
|
// used when updating to avoid multiple extra initializations
|
|
|
|
|
protected boolean skipAll = false;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* synchronized (this.waiting) { this.waiting.add(meta.id); }
|
|
|
|
|
*/
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
int retriesCount = meta.retries;
|
|
|
|
|
boolean result = false;
|
|
|
|
|
while (!result && (retriesCount > 0)) {
|
|
|
|
|
log.info("Executing initializer with id {}: try {} of {}", meta.id, meta.retries-retriesCount+1, meta.retries);
|
|
|
|
|
retriesCount--;
|
|
|
|
|
result = meta.execute();
|
|
|
|
|
if ((retriesCount > 0) && !result)
|
|
|
|
|
try {
|
|
|
|
|
log.info("Executing initializer with id {}: sleeping for {}", meta.id, meta.interval);
|
|
|
|
|
Thread.sleep(meta.interval);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
log.error("Failed sleep for thread {} on initializer {}", Thread.currentThread().getId(), meta.id);
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// last try
|
|
|
|
|
if (!result)
|
|
|
|
|
meta.setCallback();
|
|
|
|
|
protected Map<String, Set<String>> initersToDeps = new HashMap<>();
|
|
|
|
|
protected Map<String, Set<String>> depsToIniters = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected InitializerMeta getMetaById(String id) {
|
|
|
|
|
synchronized (initializers) {
|
|
|
|
|
if (!initializers.containsKey(id)) {
|
|
|
|
|
InitializerMeta meta = new InitializerMeta();
|
|
|
|
|
meta.id = id;
|
|
|
|
|
initializers.put(id, meta);
|
|
|
|
|
}
|
|
|
|
|
return initializers.get(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public InitManager(BundleContext context) {
|
|
|
|
|
this.bundleContext = context;
|
|
|
|
|
|
|
|
|
|
ServiceReference<ConfigRepository> ref1 = this.bundleContext.getServiceReference(ConfigRepository.class);
|
|
|
|
|
this.configRepository = this.bundleContext.getService(ref1);
|
|
|
|
|
|
|
|
|
|
if (!getValueById(this.getClass().getName())){
|
|
|
|
|
updateById(this.getClass().getName(), true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.skipAll = getValueById(PROP_SKIP_ALL);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected InitializerMeta createMeta(String initializerMeta, Bundle bundle) {
|
|
|
|
|
InitializerMeta meta = new InitializerMeta();
|
|
|
|
|
meta.load(initializerMeta);
|
|
|
|
|
meta.bundleId = bundle.getBundleId();
|
|
|
|
|
meta.bundleContext = bundleContext;
|
|
|
|
|
|
|
|
|
|
synchronized (this.initializers) {
|
|
|
|
|
if (this.initializers.containsKey(meta.id)) {
|
|
|
|
|
this.initializers.get(meta.id).load(meta);
|
|
|
|
|
} else {
|
|
|
|
|
this.initializers.put(meta.id, meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
protected Map<String, InitializerMeta> initializers = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
return this.initializers.get(meta.id);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param initializerClass class name read by tracker from Entaxy-Initializer-Class manifest header
|
|
|
|
|
* @param bundle
|
|
|
|
|
*/
|
|
|
|
|
public void init(String initializerClass, Bundle bundle) {
|
|
|
|
|
log.info("Found initializer of class {} in bundle [{}] {}", initializerClass,
|
|
|
|
|
bundle.getBundleId(), bundle.getSymbolicName());
|
|
|
|
|
|
|
|
|
|
InitializerMeta meta = createMeta(initializerClass, bundle);
|
|
|
|
|
|
|
|
|
|
meta.runInitializer();
|
|
|
|
|
|
|
|
|
|
// Thread thread = new Thread(new Executor(meta));
|
|
|
|
|
// thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
|
|
|
|
|
// thread.start();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* protected void execute(InitializerMeta meta) {
|
|
|
|
|
if (meta.execute())
|
|
|
|
|
synchronized (this.waiting) {
|
|
|
|
|
this.waiting.remove(meta.id);
|
|
|
|
|
}
|
|
|
|
|
for (String id: this.waiting) {
|
|
|
|
|
InitializerMeta waits = getMetaById(id);
|
|
|
|
|
synchronized (waits) {
|
|
|
|
|
if (waits.canExecute())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param initializerId class name to check
|
|
|
|
|
* @return true if the initializer is already completed, false otherwise
|
|
|
|
|
*/
|
|
|
|
|
protected boolean getValueById(String initializerId) {
|
|
|
|
|
try {
|
|
|
|
|
TypedProperties tp = this.configRepository.getConfig(pid);
|
|
|
|
|
if (!tp.containsKey(initializerId))
|
|
|
|
|
return false;
|
|
|
|
|
return "true".equals(tp.get(initializerId).toString());
|
|
|
|
|
} catch (IOException | InvalidSyntaxException e) {
|
|
|
|
|
log.warn("Can't get property " + initializerId, e);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
protected List<String> waiting = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
protected class InitializerMeta implements Initializer.Callback {
|
|
|
|
|
|
|
|
|
|
String id;
|
|
|
|
|
String className;
|
|
|
|
|
boolean repeat;
|
|
|
|
|
|
|
|
|
|
// Bundle bundle;
|
|
|
|
|
long bundleId;
|
|
|
|
|
|
|
|
|
|
BundleContext bundleContext;
|
|
|
|
|
|
|
|
|
|
Map<String, InitializerMeta> dependsOn = new HashMap<>();
|
|
|
|
|
Map<String, InitializerMeta> dependedBy = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
boolean executed = false;
|
|
|
|
|
boolean toBeExecuted = true;
|
|
|
|
|
|
|
|
|
|
int retries = 1;
|
|
|
|
|
|
|
|
|
|
int interval = 1000;
|
|
|
|
|
|
|
|
|
|
InitializedImpl initializedImpl;
|
|
|
|
|
|
|
|
|
|
public void load(String initializerData) {
|
|
|
|
|
String[] data = initializerData.split("\\?");
|
|
|
|
|
|
|
|
|
|
this.className = data[0];
|
|
|
|
|
String queryString = data.length > 1 ? data[1] : "";
|
|
|
|
|
|
|
|
|
|
String[] queryStringData = queryString.split("&");
|
|
|
|
|
Map<String, String> params = new HashMap<>();
|
|
|
|
|
for (int i = 0; i < queryStringData.length; i++) {
|
|
|
|
|
String[] paramData = queryStringData[i].split("=");
|
|
|
|
|
params.put(paramData[0], paramData.length > 1 ? paramData[1] : "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.id = params.containsKey(Initializer.INITIALIZER_QUERY_STRING_PARAM_ID)
|
|
|
|
|
? (Strings.isNullOrEmpty(params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_ID))
|
|
|
|
|
? className
|
|
|
|
|
: params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_ID))
|
|
|
|
|
: className;
|
|
|
|
|
|
|
|
|
|
String dependsOn = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_DEPENDS_ON);
|
|
|
|
|
if (!Strings.isNullOrEmpty(dependsOn)) {
|
|
|
|
|
String[] dependencies = dependsOn.split(",");
|
|
|
|
|
for (int i = 0; i < dependencies.length; i++) {
|
|
|
|
|
InitializerMeta meta = InitManager.this.getMetaById(dependencies[i]);
|
|
|
|
|
addDependency(meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
String repeat = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_REPEAT);
|
|
|
|
|
this.repeat = "true".equals(repeat);
|
|
|
|
|
|
|
|
|
|
String retriesValue = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_RETRIES);
|
|
|
|
|
if (!Strings.isNullOrEmpty(retriesValue))
|
|
|
|
|
try {
|
|
|
|
|
this.retries = Integer.parseInt(retriesValue);
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("Retries parameter [{}] is not an integer", retriesValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String intervalValue = params.get(Initializer.INITIALIZER_QUERY_STRING_PARAM_INTERVAL);
|
|
|
|
|
if (!Strings.isNullOrEmpty(intervalValue))
|
|
|
|
|
try {
|
|
|
|
|
this.interval = Integer.parseInt(intervalValue);
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("Interval parameter [{}] is not an integer", intervalValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void addDependency(InitializerMeta meta) {
|
|
|
|
|
synchronized (meta) {
|
|
|
|
|
if (meta.toBeExecuted && !meta.executed)
|
|
|
|
|
synchronized (this.dependsOn) {
|
|
|
|
|
meta.addDependent(this);
|
|
|
|
|
this.dependsOn.put(meta.id, meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void load(InitializerMeta other) {
|
|
|
|
|
this.className = other.className;
|
|
|
|
|
this.bundleId = other.bundleId;
|
|
|
|
|
this.repeat = other.repeat;
|
|
|
|
|
this.retries = other.retries;
|
|
|
|
|
this.interval = other.interval;
|
|
|
|
|
this.bundleContext = other.bundleContext;
|
|
|
|
|
for (InitializerMeta meta : other.dependsOn.values())
|
|
|
|
|
this.addDependency(meta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void addDependent(InitializerMeta meta) {
|
|
|
|
|
synchronized (this.dependedBy) {
|
|
|
|
|
this.dependedBy.put(meta.id, meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isExecutedBefore() {
|
|
|
|
|
return InitManager.this.getValueById(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean canExecute() {
|
|
|
|
|
return this.dependsOn.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void notifyDependent() {
|
|
|
|
|
for (InitializerMeta meta : this.dependedBy.values())
|
|
|
|
|
meta.dependencyReady(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Bundle getBundle() {
|
|
|
|
|
return bundleContext.getBundle(bundleId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean execute() {
|
|
|
|
|
/**
|
|
|
|
|
* instantiate initializer and call "init" if no exceptions, set status to "true"
|
|
|
|
|
*
|
|
|
|
|
* @see <code>Initializer</code>
|
|
|
|
|
*/
|
|
|
|
|
Bundle bundle = getBundle();
|
|
|
|
|
BundleWiring wiring = bundle.adapt(BundleWiring.class);
|
|
|
|
|
ClassLoader cl = wiring.getClassLoader();
|
|
|
|
|
Class<?> clazz;
|
|
|
|
|
try {
|
|
|
|
|
if (!InitManager.this.skipAll) {
|
|
|
|
|
clazz = cl.loadClass(className);
|
|
|
|
|
log.info("Loaded class {}", clazz.getName());
|
|
|
|
|
Constructor<?> constructor = clazz.getConstructor();
|
|
|
|
|
Initializer initializer = (Initializer) constructor.newInstance();
|
|
|
|
|
initializer.setBundleContext(bundle.getBundleContext());
|
|
|
|
|
initializer.setInitializerId(id);
|
|
|
|
|
initializer.init();
|
|
|
|
|
}
|
|
|
|
|
InitManager.this.updateById(id, true);
|
|
|
|
|
this.executed = true;
|
|
|
|
|
this.notifyDependent();
|
|
|
|
|
return true;
|
|
|
|
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException
|
|
|
|
|
| IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
|
|
|
|
| InitializerException e) {
|
|
|
|
|
log.error("Initializer error: class=" + className
|
|
|
|
|
+ ", id=" + id + " -> [" + e.getMessage() + "] :: "
|
|
|
|
|
+ (e instanceof InitializerException ? ((InitializerException) e).getDescription() : ""), e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void registerService() {
|
|
|
|
|
this.initializedImpl = new InitializedImpl(bundleContext, id);
|
|
|
|
|
this.initializedImpl.register();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void runInitializer() {
|
|
|
|
|
Thread thread = new Thread(new Executor(this));
|
|
|
|
|
thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
|
|
|
|
|
thread.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setCallback() {
|
|
|
|
|
BundleWiring wiring = getBundle().adapt(BundleWiring.class);
|
|
|
|
|
ClassLoader cl = wiring.getClassLoader();
|
|
|
|
|
Class<?> clazz;
|
|
|
|
|
try {
|
|
|
|
|
clazz = cl.loadClass(className);
|
|
|
|
|
log.info("Loaded class {}", clazz.getName());
|
|
|
|
|
Constructor<?> constructor = clazz.getConstructor();
|
|
|
|
|
Initializer initializer = (Initializer) constructor.newInstance();
|
|
|
|
|
initializer.setCallback(this);
|
|
|
|
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException
|
|
|
|
|
| IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
|
|
|
|
// TODO Auto-generated catch block
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void dependencyReady(InitializerMeta meta) {
|
|
|
|
|
synchronized (this.dependsOn) {
|
|
|
|
|
this.dependsOn.remove(meta.id);
|
|
|
|
|
if (!this.executed && this.canExecute())
|
|
|
|
|
// this.execute();
|
|
|
|
|
this.runInitializer();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void inited(Initializer owner) {
|
|
|
|
|
log.info("Initializer with id {} notified of successful init via callback", id);
|
|
|
|
|
InitManager.this.updateById(id, true);
|
|
|
|
|
this.executed = true;
|
|
|
|
|
this.notifyDependent();
|
|
|
|
|
owner.setCallback(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected static class InitializedImpl implements Initialized {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected BundleContext bundleContext;
|
|
|
|
|
|
|
|
|
|
protected String initializerId;
|
|
|
|
|
|
|
|
|
|
protected ServiceRegistration<Initialized> serviceRegistration;
|
|
|
|
|
|
|
|
|
|
public InitializedImpl(BundleContext bundleContext, String id) {
|
|
|
|
|
this.bundleContext = bundleContext;
|
|
|
|
|
this.initializerId = id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void register() {
|
|
|
|
|
try {
|
|
|
|
|
Dictionary<String, Object> props = new Hashtable<>();
|
|
|
|
|
props.put(PROP_INITIALIZER_ID, initializerId);
|
|
|
|
|
this.serviceRegistration = bundleContext.registerService(Initialized.class, this, props);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error registering service for initializer [" + initializerId + "]", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void unregister() {
|
|
|
|
|
if (serviceRegistration != null)
|
|
|
|
|
try {
|
|
|
|
|
serviceRegistration.unregister();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error unregistering service for initializer [" + initializerId + "]", e);
|
|
|
|
|
}
|
|
|
|
|
serviceRegistration = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isRegistered() {
|
|
|
|
|
return serviceRegistration != null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static class Executor implements Runnable {
|
|
|
|
|
|
|
|
|
|
InitializerMeta meta;
|
|
|
|
|
|
|
|
|
|
public Executor(InitializerMeta meta) {
|
|
|
|
|
this.meta = meta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* check the initialization status
|
|
|
|
|
*/
|
|
|
|
|
if (meta.isExecutedBefore()) {
|
|
|
|
|
log.info("Initializer with id {} in bundle [{}] {} is already inited", meta.id,
|
|
|
|
|
meta.bundleId, meta.getBundle().getSymbolicName());
|
|
|
|
|
|
|
|
|
|
if (meta.repeat)
|
|
|
|
|
log.info("Initializer with id {} is set to be repeated", meta.id);
|
|
|
|
|
else {
|
|
|
|
|
meta.toBeExecuted = false;
|
|
|
|
|
meta.notifyDependent();
|
|
|
|
|
meta.registerService();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!meta.canExecute()) {
|
|
|
|
|
String dependsOn = meta.dependsOn.values().stream().map((m) -> m.id).collect(Collectors.joining(","));
|
|
|
|
|
log.info("Initializer with id {} is waiting for dependencies: {}", meta.id, dependsOn);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* synchronized (this.waiting) { this.waiting.add(meta.id); }
|
|
|
|
|
*/
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
int retriesCount = meta.retries;
|
|
|
|
|
boolean result = false;
|
|
|
|
|
while (!result && (retriesCount > 0)) {
|
|
|
|
|
log.info("Executing initializer with id {}: try {} of {}", meta.id, meta.retries - retriesCount + 1,
|
|
|
|
|
meta.retries);
|
|
|
|
|
retriesCount--;
|
|
|
|
|
result = meta.execute();
|
|
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
|
meta.registerService();
|
|
|
|
|
|
|
|
|
|
if ((retriesCount > 0) && !result)
|
|
|
|
|
try {
|
|
|
|
|
log.info("Executing initializer with id {}: sleeping for {}", meta.id, meta.interval);
|
|
|
|
|
Thread.sleep(meta.interval);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
log.error("Failed sleep for thread {} on initializer {}", Thread.currentThread().getId(),
|
|
|
|
|
meta.id);
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// last try
|
|
|
|
|
if (!result)
|
|
|
|
|
meta.setCallback();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected InitializerMeta getMetaById(String id) {
|
|
|
|
|
synchronized (initializers) {
|
|
|
|
|
if (!initializers.containsKey(id)) {
|
|
|
|
|
InitializerMeta meta = new InitializerMeta();
|
|
|
|
|
meta.id = id;
|
|
|
|
|
initializers.put(id, meta);
|
|
|
|
|
}
|
|
|
|
|
return initializers.get(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public InitManager(BundleContext context) {
|
|
|
|
|
this.bundleContext = context;
|
|
|
|
|
|
|
|
|
|
ServiceReference<ConfigRepository> ref1 = this.bundleContext.getServiceReference(ConfigRepository.class);
|
|
|
|
|
this.configRepository = this.bundleContext.getService(ref1);
|
|
|
|
|
|
|
|
|
|
if (!getValueById(this.getClass().getName())) {
|
|
|
|
|
updateById(this.getClass().getName(), true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.skipAll = getValueById(PROP_SKIP_ALL);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected InitializerMeta createMeta(String initializerMeta, Bundle bundle) {
|
|
|
|
|
InitializerMeta meta = new InitializerMeta();
|
|
|
|
|
meta.load(initializerMeta);
|
|
|
|
|
meta.bundleId = bundle.getBundleId();
|
|
|
|
|
meta.bundleContext = bundleContext;
|
|
|
|
|
|
|
|
|
|
synchronized (this.initializers) {
|
|
|
|
|
if (this.initializers.containsKey(meta.id)) {
|
|
|
|
|
this.initializers.get(meta.id).load(meta);
|
|
|
|
|
} else {
|
|
|
|
|
this.initializers.put(meta.id, meta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.initializers.get(meta.id);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param initializerClass class name read by tracker from Entaxy-Initializer-Class manifest
|
|
|
|
|
* header
|
|
|
|
|
* @param bundle
|
|
|
|
|
*/
|
|
|
|
|
public void init(String initializerClass, Bundle bundle) {
|
|
|
|
|
log.info("Found initializer of class {} in bundle [{}] {}", initializerClass,
|
|
|
|
|
bundle.getBundleId(), bundle.getSymbolicName());
|
|
|
|
|
|
|
|
|
|
InitializerMeta meta = createMeta(initializerClass, bundle);
|
|
|
|
|
|
|
|
|
|
meta.runInitializer();
|
|
|
|
|
|
|
|
|
|
// Thread thread = new Thread(new Executor(meta));
|
|
|
|
|
// thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
|
|
|
|
|
// thread.start();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* protected void execute(InitializerMeta meta) { if (meta.execute()) synchronized
|
|
|
|
|
* (this.waiting) { this.waiting.remove(meta.id); } for (String id: this.waiting) {
|
|
|
|
|
* InitializerMeta waits = getMetaById(id); synchronized (waits) { if (waits.canExecute()) } } }
|
|
|
|
|
*/
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param initializerId class name to check
|
|
|
|
|
* @return true if the initializer is already completed, false otherwise
|
|
|
|
|
*/
|
|
|
|
|
protected boolean getValueById(String initializerId) {
|
|
|
|
|
try {
|
|
|
|
|
TypedProperties tp = this.configRepository.getConfig(pid);
|
|
|
|
|
if (!tp.containsKey(initializerId))
|
|
|
|
|
return false;
|
|
|
|
|
return "true".equals(tp.get(initializerId).toString());
|
|
|
|
|
} catch (IOException | InvalidSyntaxException e) {
|
|
|
|
|
log.warn("Can't get property " + initializerId, e);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void updateById(String initializerId, boolean value) {
|
|
|
|
|
log.info("Updating by id {}", initializerId);
|
|
|
|
|
try {
|
|
|
|
|
TypedProperties tp = this.configRepository.getConfig(pid);
|
|
|
|
|
tp.put(initializerId, value ? "true" : "false");
|
|
|
|
|
this.configRepository.update(pid, tp);
|
|
|
|
|
} catch (IOException | InvalidSyntaxException e) {
|
|
|
|
|
log.warn("Can't update property " + initializerId, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void updateById(String initializerId, boolean value) {
|
|
|
|
|
log.info("Updating by id {}", initializerId);
|
|
|
|
|
try {
|
|
|
|
|
TypedProperties tp = this.configRepository.getConfig(pid);
|
|
|
|
|
tp.put(initializerId, value?"true":"false");
|
|
|
|
|
this.configRepository.update(pid, tp);
|
|
|
|
|
} catch (IOException | InvalidSyntaxException e) {
|
|
|
|
|
log.warn("Can't update property " + initializerId, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|