dev.sixpack.generator.Supplier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdk Show documentation
Show all versions of sdk Show documentation
SDK to develop generators part of the Sixpack solution
The newest version!
package dev.sixpack.generator;
import dev.sixpack.api.data.Item;
import dev.sixpack.api.data.Manifest;
import dev.sixpack.api.data.RegistrationResult;
import dev.sixpack.api.exception.SixpackRegistrationException;
import dev.sixpack.api.exception.SixpackFatalException;
import dev.sixpack.api.rpc.GenerationW;
import dev.sixpack.api.rpc.RegistrationW;
import dev.sixpack.api.rpc.SupplierAi;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowClientOptions;
import io.temporal.client.WorkflowExecutionAlreadyStarted;
import io.temporal.serviceclient.SimpleSslContextBuilder;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import io.temporal.worker.WorkerOptions;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.Security;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static dev.sixpack.api.rpc.NormalisationUtils.safe;
import static dev.sixpack.utils.FormatUtils.mFormat;
import static dev.sixpack.utils.Timer.sleep;
import static java.lang.management.ManagementFactory.getRuntimeMXBean;
public abstract class Supplier {
protected final Logger LOGGER;
private final SupplierConfig config;
private final String supplierName;
private WorkflowClient workflowClient;
WorkerFactory factory;
private Manifest manifest;
protected Supplier() {
this(SupplierConfig.fromEnv());
}
protected Supplier(SupplierConfig config) {
Security.addProvider(new BouncyCastleProvider());
this.config = config;
this.supplierName = supplierNameAnnotation();
LOGGER = LoggerFactory.getLogger(supplierName + "Supplier");
}
final public Supplier withSixPackUrl(String sixpackUrl) {
config.setSixpackUrl(sixpackUrl);
return this;
}
final public Supplier withAccount(String account) {
config.setAccount(account);
return this;
}
final public Supplier withClientCertificatePath(String clientCertificatePath) {
config.setClientCertificatePath(clientCertificatePath);
return this;
}
final public Supplier withClientKeyPath(String clientKeyPath) {
config.setClientKeyPath(clientKeyPath);
return this;
}
final public Supplier withEnvironment(String environment) {
config.setEnvironment(environment);
return this;
}
final public Supplier withGenerators(Generator... generators) {
config.setGenerators(generators);
return this;
}
final public Supplier withOrchestrators(Class>... orchestrators) {
//noinspection unchecked
config.setOrchestrators((Class extends Orchestrator>[]) orchestrators);
return this;
}
private String supplierNameAnnotation() {
SupplierName annotation = getClass().getAnnotation(SupplierName.class);
if (annotation == null) {
throw new IllegalArgumentException(
"This supplier is not annotated with @SupplierName. This annotation is used to uniquely identify the test data supplier across the whole system landscape for one given test environment. Please agree with other teams if required what value should SupplierName have and add the annotation. A typical convention is to use a system name or team name commonly known in the organisation.");
}
return annotation.value();
}
private String getSignalisationTaskQueue() {
return safe(manifest.getEnvironment() + "_" + manifest.getSupplier());
}
private String getFactoryTaskQueue(String item) {
return safe(manifest.getEnvironment() + "_" + manifest.getSupplier() + "_" + item);
}
private final Object lock = new Object();
final public void bootstrap() {
config.verify();
LOGGER.info("Starting supplier with sixpackUrl: {}, account: {}, environment: {}, certificatePath: {}, keyPath: {}",
config.getSixpackUrl(),
config.getAccount(),
config.getEnvironment(),
config.getClientCertificatePath(),
config.getClientKeyPath());
Instant startupStartTime = Instant.ofEpochMilli(getRuntimeMXBean().getStartTime());
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOGGER.warn("Shutdown signal received, stopping ...");
// We have nothing to as of now but this may change
}, "hook"));
inspect();
Runnable mainLoop = () -> {
while (true) {
try {
start();
Instant startupEndTime = Instant.now();
LOGGER.info("Connected");
LOGGER.info("Started in {} ms",
Duration.between(startupStartTime, startupEndTime).toMillis());
register();
synchronized (lock) {
lock.wait(); // wait indefinitely
}
} catch (SixpackRegistrationException e) {
LOGGER.error(e.getMessage());
handleRetry();
} catch (SixpackFatalException e) {
printCause(e);
LOGGER.error("Exiting the main loop");
System.exit(1);
} catch (Throwable e) {
LOGGER.error("Sixpack unknown exception caught at top level");
printCause(e);
handleRetry();
}
}
};
new Thread(mainLoop, "SixpackBootstrap").start();
}
private void handleRetry() {
LOGGER.warn("Closing connection to Sixpack ...");
factory.shutdownNow();
LOGGER.info("Retrying in 10 seconds ...");
sleep(10);
}
private void inspect() {
Map factories = new HashMap<>();
if (config.getGenerators() != null) {
Arrays.stream(config.getGenerators())
.map(generator -> new GeneratorRecordBuilder(
config.getEnvironment(),
supplierNameAnnotation(),
generator)
.build())
.forEach(factoryRecord -> {
LOGGER.info("Analysed generator: {} class: {} input: {} output: {}",
factoryRecord.getItem(),
factoryRecord.getGenerator().getClass().getSimpleName(),
factoryRecord.getInput().getSimpleName(),
factoryRecord.getOutput().getSimpleName());
factories.put(factoryRecord.getItem(), factoryRecord);
});
}
if (config.getOrchestrators() != null) {
Arrays.stream(config.getOrchestrators())
.map(orchestrator -> new OrchestratorRecordBuilder(
config.getEnvironment(),
supplierNameAnnotation(),
orchestrator)
.build())
.forEach(factoryRecord -> {
LOGGER.info("Analysed orchestrator: {} class: {} input: {} output: {}",
factoryRecord.getItem(),
factoryRecord.getOrchestrator().getSimpleName(),
factoryRecord.getInput().getSimpleName(),
factoryRecord.getOutput().getSimpleName());
factories.put(factoryRecord.getItem(), factoryRecord);
});
}
manifest = Manifest.builder()
.environment(config.getEnvironment())
.supplier(supplierName)
.items(new SchemaBuilder(factories).build())
.build();
LOGGER.info("Running supplier {} on environment {} with following items: {}",
manifest.getSupplier(),
manifest.getEnvironment(),
manifest.getItems().stream().map(Item::getName).collect(Collectors.joining(", ")));
}
private void start() {
LOGGER.info("Trying to connect to Sixpack ...");
WorkflowServiceStubs service = WorkflowServiceStubs.newServiceStubs(WorkflowServiceStubsOptions.newBuilder()
.setSslContext(buildSslContext())
.setTarget(config.getSixpackUrl())
.setRpcLongPollTimeout(Duration.ofSeconds(55))
.validateAndBuildWithDefaults());
workflowClient = WorkflowClient.newInstance(service, WorkflowClientOptions.newBuilder()
.setNamespace(config.getAccount())
.build());
factory = WorkerFactory.newInstance(workflowClient);
Worker signalisationWorker = factory.newWorker(
getSignalisationTaskQueue(),
WorkerOptions.newBuilder()
.setMaxConcurrentActivityExecutionSize(5)
.validateAndBuildWithDefaults());
signalisationWorker.registerActivitiesImplementations(new SupplierAi(supplierName));
manifest.getItems().forEach(item -> {
Generator generator = item.getFactoryRecord().getGenerator();
if (generator != null) {
generator.setEnvironment(config.getEnvironment());
}
Worker factoryWorker = factory.newWorker(
getFactoryTaskQueue(item.getName()),
WorkerOptions.newBuilder()
.setMaxConcurrentActivityExecutionSize(1)
.validateAndBuildWithDefaults());
factoryWorker.registerWorkflowImplementationFactory(
GenerationW.class,
() -> {
LOGGER.trace("Creating a new factory instance for {}", item.getFactoryRecord().getItem());
return new GenerationWi(item.getFactoryRecord());
});
if (generator != null) {
factoryWorker.registerActivitiesImplementations(new GeneratorController(item.getFactoryRecord()));
}
});
factory.start();
}
private void register() {
RegistrationW registrationWorkflow = workflowClient.newWorkflowStub(RegistrationW.class,
RegistrationW.Options.get(manifest.getEnvironment(), manifest.getSupplier()));
LOGGER.info("Registering supplier's catalogue for environment {}", manifest.getEnvironment());
try {
RegistrationResult result = registrationWorkflow.register(config.getAccount(), manifest);
result.getWarnings().forEach(LOGGER::warn);
if (!result.getErrors().isEmpty()) {
LOGGER.error("Server returned fatal errors during registration:");
result.getErrors().forEach(LOGGER::error);
throw new SixpackFatalException("Registration failed with fatal errors");
}
} catch (WorkflowExecutionAlreadyStarted e) {
throw new SixpackRegistrationException("Server did not yet process the previous registration");
} catch (Exception e) {
throw new SixpackRegistrationException("Communication with server failed: " + e.getMessage());
}
LOGGER.info("Registration was successful");
}
private SslContext buildSslContext() {
try {
return SimpleSslContextBuilder.forPKCS8(
getFileAsStreamOrFatal(config.getClientCertificatePath()),
getFileAsStreamOrFatal(config.getClientKeyPath()))
.setUseInsecureTrustManager(true)
.build();
} catch (SSLException e) {
throw new IllegalArgumentException(
mFormat("Failed building SSL Context with files looked up at {} and {}",
config.getClientCertificatePath(),
config.getClientKeyPath()), e);
}
}
private static InputStream getFileAsStreamOrFatal(String path) {
try {
return new FileInputStream(path);
} catch (FileNotFoundException e) {
throw new SixpackFatalException(mFormat("Could not find the file {}", path), e);
}
}
private void printCause(Throwable e) {
if (e.getCause() != null) {
printCause(e.getCause());
} else {
LOGGER.error(e.getMessage());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy