Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ private DBOSConfig buildConfig(DBOSProperties props, String springAppName) {
config = config.withConductorDomain(props.getConductor().getDomain());
}
config = config.withConductorMetadataOnlyMode(props.getConductor().isMetadataOnlyMode());
if (props.getTransactEnterpriseKey() != null) {
config = config.withTransactEnterpriseKey(props.getTransactEnterpriseKey());
}
if (props.getApplication().getVersion() != null) {
config = config.withAppVersion(props.getApplication().getVersion());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public class DBOSProperties {
/** Executor ID for this instance. */
private String executorId;

/**
* The DBOS key that entitles this application to Transact Enterprise features. When not set, the
* Conductor key stands in for it.
*/
private String transactEnterpriseKey;

/** Whether to enable workflow patching. Defaults to {@code false}. */
private boolean enablePatching = false;

Expand Down Expand Up @@ -242,6 +248,14 @@ public void setExecutorId(String executorId) {
this.executorId = executorId;
}

public String getTransactEnterpriseKey() {
return transactEnterpriseKey;
}

public void setTransactEnterpriseKey(String transactEnterpriseKey) {
this.transactEnterpriseKey = transactEnterpriseKey;
}

public boolean isEnablePatching() {
return enablePatching;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ void dbosConfigReflectsAppNameProperty() {
});
}

@Test
void emptyKeysAreUnset() {
// As from placeholders whose environment variables are unset: no Conductor connection, and no
// enterprise key.
runner
.withPropertyValues(
"dbos.conductor.key=", "dbos.conductor.domain=", "dbos.transact-enterprise-key=")
.run(
context -> {
assertThat(context).hasNotFailed();
var config = context.getBean(DBOSConfig.class);
assertThat(config.conductorKey()).isNull();
assertThat(config.conductorDomain()).isNull();
assertThat(config.transactEnterpriseKey()).isNull();
});
}

@Test
void dbosConfigReflectsDatasourceProperties() {
runner
Expand All @@ -73,6 +90,7 @@ void dbosConfigReflectsOptionalProperties() {
"dbos.conductor.key=my-key",
"dbos.conductor.domain=my-domain",
"dbos.conductor.metadata-only-mode=true",
"dbos.transact-enterprise-key=my-enterprise-key",
"dbos.application.version=1.2.3",
"dbos.executor-id=exec-1",
"dbos.datasource.schema=my_schema",
Expand All @@ -88,6 +106,7 @@ void dbosConfigReflectsOptionalProperties() {
assertThat(config.conductorKey()).isEqualTo("my-key");
assertThat(config.conductorDomain()).isEqualTo("my-domain");
assertThat(config.conductorMetadataOnlyMode()).isTrue();
assertThat(config.transactEnterpriseKey()).isEqualTo("my-enterprise-key");
assertThat(config.appVersion()).isEqualTo("1.2.3");
assertThat(config.executorId()).isEqualTo("exec-1");
assertThat(config.databaseSchema()).isEqualTo("my_schema");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ void defaults() {
assertNull(props.getConductor().getDomain());
assertFalse(props.getConductor().isMetadataOnlyMode());
assertNull(props.getExecutorId());
assertNull(props.getTransactEnterpriseKey());
assertNull(props.getDatasource().getSchema());
assertNull(props.getSchedulerPollingInterval());
assertNotNull(props.getDatasource());
Expand Down Expand Up @@ -103,6 +104,12 @@ void bindsConductorProperties() {
assertTrue(props.getConductor().isMetadataOnlyMode());
}

@Test
void bindsTheTransactEnterpriseKey() {
var props = bind(Map.of("dbos.transact-enterprise-key", "my-enterprise-key"));
assertEquals("my-enterprise-key", props.getTransactEnterpriseKey());
}

@Test
void bindsIdentityProperties() {
var props = bind(Map.of("dbos.executor-id", "exec-1"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
package dev.dbos.transact.conductor;

import dev.dbos.transact.DBOS;
import dev.dbos.transact.internal.EnterpriseProviders;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.regex.Pattern;

/** Finds the {@link ConductorProvider} on the class path. */
public final class ConductorProviders {
static final String ENTERPRISE_ARTIFACT = "dev.dbos:transact-enterprise";

private static final Pattern RELEASE = Pattern.compile("\\d+\\.\\d+\\.\\d+");
private static final Pattern MINOR = Pattern.compile("(\\d+\\.\\d+)\\..*");
// Connecting to Conductor needs the enterprise artifact, but no entitlement.
private static final String FEATURE = "Connecting to DBOS Conductor";

private ConductorProviders() {}

Expand All @@ -27,66 +20,10 @@ private ConductorProviders() {}
* initialize
*/
public static ConductorProvider load() {
var loaders = new ArrayList<ClassLoader>();
var context = Thread.currentThread().getContextClassLoader();
if (context != null) {
loaders.add(context);
}
var own = ConductorProvider.class.getClassLoader();
if (own != context) {
loaders.add(own);
}

// One instance per provider class, constructed once: a loader that delegates to its parent sees
// the parent's registrations too, so duplicates are dropped before they are instantiated.
var providers = new LinkedHashMap<Class<?>, ConductorProvider>();
try {
for (var loader : loaders) {
ServiceLoader.load(ConductorProvider.class, loader).stream()
.filter(provider -> !providers.containsKey(provider.type()))
.forEach(provider -> providers.put(provider.type(), provider.get()));
}
} catch (ServiceConfigurationError e) {
// A provider's constructor rejected this DBOS, e.g. for a version mismatch. Its own message
// says why; ServiceLoader's names the provider.
var cause = e.getCause();
var message =
cause != null && cause.getMessage() != null
? e.getMessage() + ": " + cause.getMessage()
: e.getMessage();
throw new IllegalStateException(message, e);
}
return select(List.copyOf(providers.values()), DBOS.version());
return EnterpriseProviders.load(ConductorProvider.class, FEATURE);
}

static ConductorProvider select(List<ConductorProvider> providers, String version) {
if (providers.isEmpty()) {
throw new IllegalStateException(
"Connecting to DBOS Conductor requires "
+ ENTERPRISE_ARTIFACT
+ " on the class path. "
+ versionAdvice(version));
}
if (providers.size() > 1) {
var names = providers.stream().map(p -> p.getClass().getName()).toList();
throw new IllegalStateException(
"Found more than one DBOS Conductor provider on the class path: " + names);
}
return providers.get(0);
}

// Previews are numbered by commits in each repo, so only a release names the exact version.
private static String versionAdvice(String version) {
if (version != null && RELEASE.matcher(version).matches()) {
return String.format(
"Add %s:%s, the same version as dev.dbos:transact", ENTERPRISE_ARTIFACT, version);
}
var minor = version == null ? null : MINOR.matcher(version);
if (minor != null && minor.matches()) {
return String.format(
"Add a %s %s build, as dev.dbos:transact is %s",
ENTERPRISE_ARTIFACT, minor.group(1), version);
}
return "Add the " + ENTERPRISE_ARTIFACT + " version that matches dev.dbos:transact";
return EnterpriseProviders.select(ConductorProvider.class, providers, version, FEATURE);
}
}
Loading
Loading