io.datakernel.trigger.TriggersModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datakernel-boot Show documentation
Show all versions of datakernel-boot Show documentation
An intelligent way of booting complex applications and services according to their dependencies
/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.datakernel.trigger;
import com.google.inject.*;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.spi.ProvisionListener;
import io.datakernel.annotation.Nullable;
import io.datakernel.service.BlockingService;
import io.datakernel.service.ServiceGraph;
import io.datakernel.util.Initializable;
import io.datakernel.util.guice.GuiceUtils;
import io.datakernel.util.guice.OptionalDependency;
import io.datakernel.util.guice.OptionalInitializer;
import io.datakernel.util.guice.RequiredDependency;
import io.datakernel.worker.WorkerPool;
import io.datakernel.worker.WorkerPoolModule;
import io.datakernel.worker.WorkerPools;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import static com.google.common.collect.Iterators.getLast;
import static io.datakernel.util.guice.GuiceUtils.*;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
public final class TriggersModule extends AbstractModule implements Initializable {
private Function, String> keyToString = GuiceUtils::prettyPrintSimpleKeyName;
private final Set> singletonKeys = new HashSet<>();
private final Set> workerKeys = new HashSet<>();
private final LinkedHashSet> currentlyProvidingSingletonKeys = new LinkedHashSet<>();
private final LinkedHashMap, KeyWithWorkerData> currentlyProvidingWorkerKeys = new LinkedHashMap<>();
private final Map, List> singletonRegistryRecords = new HashMap<>();
private final Map> workerRegistryRecords = new HashMap<>();
private final Map, Set>> classSettings = new LinkedHashMap<>();
private final Map, Set>> keySettings = new LinkedHashMap<>();
private static final class TriggerConfig {
private final Severity severity;
private final String name;
private final Function triggerFunction;
TriggerConfig(Severity severity, String name,
Function triggerFunction) {
this.severity = severity;
this.name = name;
this.triggerFunction = triggerFunction;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TriggerConfig> that = (TriggerConfig>) o;
return severity == that.severity &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(severity, name);
}
}
private static final class KeyWithWorkerData {
private final Key> key;
@Nullable
private final WorkerPool pool;
private final int workerId;
private KeyWithWorkerData(Key> key) {
this(key, null, -1);
}
private KeyWithWorkerData(Key> key, @Nullable WorkerPool pool, int workerId) {
this.key = key;
this.pool = pool;
this.workerId = workerId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeyWithWorkerData that = (KeyWithWorkerData) o;
return workerId == that.workerId
&& key.equals(that.key)
&& (pool != null ? pool.equals(that.pool) : that.pool == null);
}
@Override
public int hashCode() {
return 31 * (31 * key.hashCode() + (pool != null ? pool.hashCode() : 0)) + workerId;
}
@Override
public String toString() {
return "KeyWithWorkerData{key=" + key + ", pool=" + pool + ", workerId=" + workerId + '}';
}
}
private static final class TriggerRegistryRecord {
private final Severity severity;
private final String name;
private final Supplier triggerFunction;
private TriggerRegistryRecord(Severity severity, String name, Supplier triggerFunction) {
this.severity = severity;
this.name = name;
this.triggerFunction = triggerFunction;
}
}
private final class TriggerRegistryRecorder implements TriggerRegistry {
private final Key> key;
private final List records = new ArrayList<>();
private TriggerRegistryRecorder(Key> key) {
this.key = key;
}
@Override
public Key> getComponentKey() {
return key;
}
@Override
public String getComponentName() {
return keyToString.apply(key);
}
@Override
public void add(Severity severity, String name, Supplier triggerFunction) {
records.add(new TriggerRegistryRecord(severity, name, triggerFunction));
}
}
public interface TriggersModuleService extends BlockingService {
}
private TriggersModule() {
}
public static TriggersModule create() {
return new TriggersModule();
}
public TriggersModule withNaming(Function, String> keyToString) {
this.keyToString = keyToString;
return this;
}
public TriggersModule with(Class type, Severity severity, String name, Function triggerFunction) {
Set> triggerConfigs = classSettings.computeIfAbsent(type, $ -> new LinkedHashSet<>());
if (!triggerConfigs.add(new TriggerConfig<>(severity, name, triggerFunction))) {
throw new IllegalArgumentException("Cannot assign duplicate triggers");
}
return this;
}
public TriggersModule with(Key key, Severity severity, String name, Function triggerFunction) {
Set> triggerConfigs = keySettings.computeIfAbsent(key, $ -> new LinkedHashSet<>());
if (!triggerConfigs.add(new TriggerConfig<>(severity, name, triggerFunction))) {
throw new IllegalArgumentException("Cannot assign duplicate triggers");
}
return this;
}
@Override
protected void configure() {
bind(new TypeLiteral>() {}).asEagerSingleton();
bind(new TypeLiteral>() {}).asEagerSingleton();
bindListener(new AbstractMatcher>() {
@Override
public boolean matches(Binding> binding) {
return isSingleton(binding);
}
}, new ProvisionListener() {
@Override
public void onProvision(ProvisionInvocation provision) {
synchronized (TriggersModule.this) {
Key key = provision.getBinding().getKey();
currentlyProvidingSingletonKeys.add(key);
if (provision.provision() != null) {
singletonKeys.add(key);
}
currentlyProvidingSingletonKeys.remove(key);
}
}
});
bindListener(new AbstractMatcher>() {
@Override
public boolean matches(Binding> binding) {
return WorkerPoolModule.isWorkerScope(binding);
}
}, new ProvisionListener() {
@Override
public void onProvision(ProvisionInvocation provision) {
synchronized (TriggersModule.this) {
Key key = provision.getBinding().getKey();
Integer workerId = extractWorkerId(provision.getBinding());
WorkerPool workerPool = extractWorkerPool(provision.getBinding());
assert workerId != null && workerPool != null : provision.getBinding();
currentlyProvidingWorkerKeys.put(key, new KeyWithWorkerData(key, workerPool, workerId));
if (provision.provision() != null) {
workerKeys.add(provision.getBinding().getKey());
}
currentlyProvidingWorkerKeys.remove(key);
}
}
});
bindListener(new AbstractMatcher>() {
@Override
public boolean matches(Binding> binding) {
return binding.getKey().equals(Key.get(TriggerRegistry.class));
}
}, new ProvisionListener() {
@SuppressWarnings("deprecation")
@Override
public void onProvision(ProvisionInvocation provision) {
synchronized (TriggersModule.this) {
TriggerRegistryRecorder triggerRegistry = (TriggerRegistryRecorder) provision.provision();
if (triggerRegistry == null) {
return;
}
for (int i = provision.getDependencyChain().size() - 1; i >= 0; i--) {
Key> key = provision.getDependencyChain().get(i).getDependency().getKey();
if (currentlyProvidingSingletonKeys.contains(key)) {
singletonRegistryRecords.computeIfAbsent(key, $ -> new ArrayList<>()).add(triggerRegistry);
break;
}
KeyWithWorkerData kwwd = currentlyProvidingWorkerKeys.get(key);
if (kwwd != null) {
workerRegistryRecords.computeIfAbsent(kwwd, $ -> new ArrayList<>()).add(triggerRegistry);
break;
}
}
}
}
});
}
@Provides
@Singleton
TriggersModuleService service(Injector injector, Triggers triggers,
OptionalInitializer optionalInitializer) {
optionalInitializer.accept(this);
return new TriggersModuleService() {
@Override
public void start() {
initialize(injector);
}
@Override
public void stop() {
}
};
}
@Provides
@Singleton
Triggers getTriggersWatcher(Injector injector) {
return Triggers.create();
}
@Provides
TriggerRegistry getTriggersRegistry() {
return new TriggerRegistryRecorder(getLast(currentlyProvidingSingletonKeys.iterator()));
}
@SuppressWarnings("unchecked")
private void initialize(Injector injector) {
Triggers triggers = injector.getInstance(Triggers.class);
Map> triggersMap = new LinkedHashMap<>();
// register singletons
for (Key> k : singletonKeys) {
Key
© 2015 - 2024 Weber Informatics LLC | Privacy Policy