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
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
*
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this material 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.temporal.spring.boot.autoconfigure;

import io.temporal.common.converter.DataConverter;
import java.util.List;
import javax.annotation.Nullable;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;

class AutoConfigurationUtils {
@Nullable
static DataConverter choseDataConverter(
List<DataConverter> dataConverters, DataConverter mainDataConverter) {
DataConverter chosenDataConverter = null;
if (dataConverters.size() == 1) {
chosenDataConverter = dataConverters.get(0);
} else if (dataConverters.size() > 1) {
if (mainDataConverter != null) {
chosenDataConverter = mainDataConverter;
} else {
throw new NoUniqueBeanDefinitionException(
DataConverter.class,
dataConverters.size(),
"Several DataConverter beans found in the Spring context. "
+ "Explicitly name 'mainDataConverter' the one bean "
+ "that should be used by Temporal Spring Boot AutoConfiguration.");
}
}
return chosenDataConverter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
*
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this material 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.temporal.spring.boot.autoconfigure;

import com.uber.m3.tally.RootScopeBuilder;
import com.uber.m3.tally.Scope;
import com.uber.m3.tally.StatsReporter;
import io.micrometer.core.instrument.MeterRegistry;
import io.temporal.common.reporter.MicrometerClientStatsReporter;
import javax.annotation.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@AutoConfigureAfter(
name =
"org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration")
Comment on lines +36 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@AutoConfigureAfter(
name =
"org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration")
@AutoConfigureAfter(CompositeMeterRegistryAutoConfiguration.class)

Or do we not want to import and static init that class here?

Copy link
Contributor Author

@Spikhalskiy Spikhalskiy Jan 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know if the class will be even present in the classpath here.

@ConditionalOnBean(MeterRegistry.class)
public class MetricsScopeAutoConfiguration {
@Bean(name = "temporalMetricsScope", destroyMethod = "close")
public Scope scope(
// Spring Boot configures and exposes Micrometer MeterRegistry bean in the
// spring-boot-starter-actuator dependency
@Autowired(required = false) @Nullable MeterRegistry meterRegistry) {
StatsReporter reporter = new MicrometerClientStatsReporter(meterRegistry);
return new RootScopeBuilder()
.reporter(reporter)
.reportEvery(com.uber.m3.util.Duration.ofSeconds(10));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
Expand Down Expand Up @@ -78,22 +77,8 @@ public NamespaceTemplate rootNamespaceTemplate(
@Autowired(required = false) @Nullable Tracer otTracer,
@Qualifier("temporalTestWorkflowEnvironmentAdapter") @Autowired(required = false) @Nullable
TestWorkflowEnvironmentAdapter testWorkflowEnvironment) {

DataConverter chosenDataConverter = null;
if (dataConverters.size() == 1) {
chosenDataConverter = dataConverters.get(0);
} else if (dataConverters.size() > 1) {
if (mainDataConverter != null) {
chosenDataConverter = mainDataConverter;
} else {
throw new NoUniqueBeanDefinitionException(
DataConverter.class,
dataConverters.size(),
"Several DataConverter beans found in the Spring context. "
+ "Explicitly name 'mainDataConverter' the one bean "
+ "that should be used by Temporal Spring Boot AutoConfiguration.");
}
}
DataConverter chosenDataConverter =
AutoConfigurationUtils.choseDataConverter(dataConverters, mainDataConverter);
return new NamespaceTemplate(
properties,
properties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

package io.temporal.spring.boot.autoconfigure;

import io.micrometer.core.instrument.MeterRegistry;
import com.uber.m3.tally.Scope;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.spring.boot.autoconfigure.properties.TemporalProperties;
import io.temporal.spring.boot.autoconfigure.template.ServiceStubsTemplate;
Expand All @@ -37,22 +37,18 @@
@Configuration
@EnableConfigurationProperties(TemporalProperties.class)
@AutoConfigureAfter(
value = TestServerAutoConfiguration.class,
name =
"org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration")
value = {MetricsScopeAutoConfiguration.class, TestServerAutoConfiguration.class})
@ConditionalOnExpression(
"${spring.temporal.test-server.enabled:false} || '${spring.temporal.connection.target:}'.length() > 0")
public class ServiceStubsAutoConfiguration {
@Bean(name = "temporalServiceStubsTemplate")
public ServiceStubsTemplate serviceStubsTemplate(
TemporalProperties properties,
// Spring Boot configures and exposes Micrometer MeterRegistry bean in the
// spring-boot-starter-actuator dependency
@Autowired(required = false) @Nullable MeterRegistry meterRegistry,
@Qualifier("temporalMetricsScope") @Autowired(required = false) @Nullable Scope metricsScope,
@Qualifier("temporalTestWorkflowEnvironmentAdapter") @Autowired(required = false) @Nullable
TestWorkflowEnvironmentAdapter testWorkflowEnvironment) {
return new ServiceStubsTemplate(
properties.getConnection(), meterRegistry, testWorkflowEnvironment);
properties.getConnection(), metricsScope, testWorkflowEnvironment);
}

@Bean(name = "temporalWorkflowServiceStubs")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@

package io.temporal.spring.boot.autoconfigure;

import com.uber.m3.tally.Scope;
import io.opentracing.Tracer;
import io.temporal.common.converter.DataConverter;
import io.temporal.spring.boot.autoconfigure.properties.TemporalProperties;
import io.temporal.spring.boot.autoconfigure.template.ClientTemplate;
import io.temporal.spring.boot.autoconfigure.template.TestWorkflowEnvironmentAdapter;
import io.temporal.testing.TestEnvironmentOptions;
import io.temporal.testing.TestWorkflowEnvironment;
import java.util.List;
import javax.annotation.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -38,6 +47,7 @@
prefix = "spring.temporal",
name = "test-server.enabled",
havingValue = "true")
@AutoConfigureAfter({OpenTracingAutoConfiguration.class, MetricsScopeAutoConfiguration.class})
public class TestServerAutoConfiguration {
@Bean(name = "temporalTestWorkflowEnvironmentAdapter")
public TestWorkflowEnvironmentAdapter testTestWorkflowEnvironmentAdapter(
Expand All @@ -47,7 +57,25 @@ public TestWorkflowEnvironmentAdapter testTestWorkflowEnvironmentAdapter(
}

@Bean(name = "temporalTestWorkflowEnvironment", destroyMethod = "close")
public TestWorkflowEnvironment testWorkflowEnvironment() {
return TestWorkflowEnvironment.newInstance();
public TestWorkflowEnvironment testWorkflowEnvironment(
TemporalProperties properties,
@Qualifier("temporalMetricsScope") @Autowired(required = false) @Nullable Scope metricsScope,
@Autowired List<DataConverter> dataConverters,
@Qualifier("mainDataConverter") @Autowired(required = false) @Nullable
DataConverter mainDataConverter,
@Autowired(required = false) @Nullable Tracer otTracer) {
DataConverter chosenDataConverter =
AutoConfigurationUtils.choseDataConverter(dataConverters, mainDataConverter);
ClientTemplate clientTemplate =
new ClientTemplate(properties.getNamespace(), chosenDataConverter, otTracer, null, null);

TestEnvironmentOptions.Builder options =
TestEnvironmentOptions.newBuilder()
.setWorkflowClientOptions(clientTemplate.getWorkflowClientOptions());
if (metricsScope != null) {
options.setMetricsScope(metricsScope);
}

return TestWorkflowEnvironment.newInstance(options.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,38 @@

package io.temporal.spring.boot.autoconfigure.template;

import com.google.common.base.Preconditions;
import io.opentracing.Tracer;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowClientOptions;
import io.temporal.common.converter.DataConverter;
import io.temporal.opentracing.OpenTracingClientInterceptor;
import io.temporal.opentracing.OpenTracingOptions;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.spring.boot.autoconfigure.properties.NamespaceProperties;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class ClientTemplate {
private final @Nonnull NamespaceProperties namespaceProperties;
private final @Nonnull WorkflowServiceStubs workflowServiceStubs;
private final @Nonnull String namespace;
private final @Nullable DataConverter dataConverter;
private final @Nullable Tracer tracer;
private final @Nullable WorkflowServiceStubs workflowServiceStubs;
// if not null, we work with an environment with defined test server
private final @Nullable TestWorkflowEnvironmentAdapter testWorkflowEnvironment;

private WorkflowClient workflowClient;

public ClientTemplate(
@Nonnull NamespaceProperties namespaceProperties,
@Nonnull WorkflowServiceStubs workflowServiceStubs,
@Nonnull String namespace,
@Nullable DataConverter dataConverter,
@Nullable Tracer tracer,
@Nullable WorkflowServiceStubs workflowServiceStubs,
@Nullable TestWorkflowEnvironmentAdapter testWorkflowEnvironment) {
this.namespaceProperties = namespaceProperties;
this.workflowServiceStubs = workflowServiceStubs;
this.namespace = Objects.requireNonNull(namespace);
this.dataConverter = dataConverter;
this.tracer = tracer;
this.workflowServiceStubs = workflowServiceStubs;
this.testWorkflowEnvironment = testWorkflowEnvironment;
}

Expand All @@ -63,16 +64,18 @@ public WorkflowClient getWorkflowClient() {

private WorkflowClient createWorkflowClient() {
if (testWorkflowEnvironment != null) {
// TODO we should still respect the client properties here.
// Instead of overriding, we should allow the test environment to configure the client.
return testWorkflowEnvironment.getWorkflowClient();
} else {
Preconditions.checkState(
workflowServiceStubs != null, "ClientTemplate was created without workflowServiceStubs");
return WorkflowClient.newInstance(workflowServiceStubs, getWorkflowClientOptions());
}
}

public WorkflowClientOptions getWorkflowClientOptions() {
WorkflowClientOptions.Builder clientOptionsBuilder = WorkflowClientOptions.newBuilder();

if (namespaceProperties.getNamespace() != null) {
clientOptionsBuilder.setNamespace(namespaceProperties.getNamespace());
}
clientOptionsBuilder.setNamespace(namespace);

if (dataConverter != null) {
clientOptionsBuilder.setDataConverter(dataConverter);
Expand All @@ -84,8 +87,6 @@ private WorkflowClient createWorkflowClient() {
OpenTracingOptions.newBuilder().setTracer(tracer).build());
clientOptionsBuilder.setInterceptors(openTracingClientInterceptor);
}

return WorkflowClient.newInstance(
workflowServiceStubs, clientOptionsBuilder.validateAndBuildWithDefaults());
return clientOptionsBuilder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ public ClientTemplate getClientTemplate() {
if (clientTemplate == null) {
this.clientTemplate =
new ClientTemplate(
namespaceProperties,
workflowServiceStubs,
namespaceProperties.getNamespace(),
dataConverter,
tracer,
workflowServiceStubs,
testWorkflowEnvironment);
}
return clientTemplate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,8 @@

package io.temporal.spring.boot.autoconfigure.template;

import com.uber.m3.tally.RootScopeBuilder;
import com.uber.m3.tally.Scope;
import com.uber.m3.tally.StatsReporter;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.micrometer.core.instrument.MeterRegistry;
import io.temporal.common.reporter.MicrometerClientStatsReporter;
import io.temporal.serviceclient.SimpleSslContextBuilder;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
Expand All @@ -43,9 +39,12 @@
import org.springframework.util.ResourceUtils;

public class ServiceStubsTemplate {
public static final com.uber.m3.util.Duration DEFAULT_SCOPE_REPORT_INTERVAL =
com.uber.m3.util.Duration.ofSeconds(1);

private final @Nonnull ConnectionProperties connectionProperties;

private final @Nullable MeterRegistry meterRegistry;
private final @Nullable Scope metricsScope;

// if not null, we work with an environment with defined test server
private final @Nullable TestWorkflowEnvironmentAdapter testWorkflowEnvironment;
Expand All @@ -54,10 +53,10 @@ public class ServiceStubsTemplate {

public ServiceStubsTemplate(
@Nonnull ConnectionProperties connectionProperties,
@Nullable MeterRegistry meterRegistry,
@Nullable Scope metricsScope,
@Nullable TestWorkflowEnvironmentAdapter testWorkflowEnvironment) {
this.connectionProperties = connectionProperties;
this.meterRegistry = meterRegistry;
this.metricsScope = metricsScope;
this.testWorkflowEnvironment = testWorkflowEnvironment;
}

Expand Down Expand Up @@ -90,8 +89,8 @@ private WorkflowServiceStubs createServiceStubs() {

configureMTLS(connectionProperties.getMTLS(), stubsOptionsBuilder);

if (meterRegistry != null) {
stubsOptionsBuilder.setMetricsScope(createScope(meterRegistry));
if (metricsScope != null) {
stubsOptionsBuilder.setMetricsScope(metricsScope);
}

workflowServiceStubs =
Expand All @@ -103,13 +102,6 @@ private WorkflowServiceStubs createServiceStubs() {
return workflowServiceStubs;
}

private Scope createScope(@Nonnull MeterRegistry registry) {
StatsReporter reporter = new MicrometerClientStatsReporter(registry);
return new RootScopeBuilder()
.reporter(reporter)
.reportEvery(com.uber.m3.util.Duration.ofSeconds(10));
}

private void configureMTLS(
@Nullable ConnectionProperties.MTLSProperties mtlsProperties,
WorkflowServiceStubsOptions.Builder stubsOptionsBuilder) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.temporal.spring.boot.autoconfigure.TestServerAutoConfiguration,\
io.temporal.spring.boot.autoconfigure.MetricsScopeAutoConfiguration,\
io.temporal.spring.boot.autoconfigure.OpenTracingAutoConfiguration,\
io.temporal.spring.boot.autoconfigure.TestServerAutoConfiguration,\
io.temporal.spring.boot.autoconfigure.ServiceStubsAutoConfiguration,\
io.temporal.spring.boot.autoconfigure.RootNamespaceAutoConfiguration
Loading