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 @@ -17,5 +17,5 @@
package com.splunk.opamp.remotecontrol;

public interface CommandDispatcher {
void dispatch(String contentType, String body);
void dispatch(String body);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public CommandDispatcherImpl(BigDumper threadDumper) {
}

@Override
public void dispatch(String contentType, String body) {
public void dispatch(String body) {
String[] parts = body.split("\n");
if (parts.length == 0) {
logger.warning("Missing useful command body.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

public class NoOpCommandDispatcher implements CommandDispatcher {
@Override
public void dispatch(String contentType, String body) {
public void dispatch(String body) {
// nop
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ void dispatchesThreadDumpWithJobIdCountAndInterval() {
when(threadDumper.startPeriodicDumper("job-123", 3, Duration.ofMillis(250))).thenReturn(true);
CommandDispatcher dispatcher = new CommandDispatcherImpl(threadDumper);

dispatcher.dispatch("text/plain", "thread.dump\njob-123\n3\n250");
dispatcher.dispatch("thread.dump\njob-123\n3\n250");

verify(threadDumper).startPeriodicDumper("job-123", 3, Duration.ofMillis(250));
}
Expand All @@ -43,7 +43,7 @@ void acceptsMaximumThreadDumpCountAndInterval() {
BigDumper threadDumper = mock(BigDumper.class);
CommandDispatcher dispatcher = new CommandDispatcherImpl(threadDumper);

dispatcher.dispatch("text/plain", "thread.dump\njob-123\n100\n300000");
dispatcher.dispatch("thread.dump\njob-123\n100\n300000");

verify(threadDumper).startPeriodicDumper("job-123", 100, Duration.ofMinutes(5));
}
Expand All @@ -54,7 +54,7 @@ void usesThreadDumpDefaults() {
when(threadDumper.startPeriodicDumper("job-123", 1, Duration.ofMillis(1000))).thenReturn(true);
CommandDispatcher dispatcher = new CommandDispatcherImpl(threadDumper);

dispatcher.dispatch("text/plain", "thread.dump\r\njob-123\r\n");
dispatcher.dispatch("thread.dump\r\njob-123\r\n");

verify(threadDumper).startPeriodicDumper("job-123", 1, Duration.ofMillis(1000));
}
Expand All @@ -64,7 +64,7 @@ void rejectsMissingJobId() {
BigDumper threadDumper = mock(BigDumper.class);
CommandDispatcher dispatcher = new CommandDispatcherImpl(threadDumper);

dispatcher.dispatch("text/plain", "thread.dump");
dispatcher.dispatch("thread.dump");

verifyNoInteractions(threadDumper);
}
Expand All @@ -74,11 +74,10 @@ void rejectsInvalidCountAndIntervalWithoutThrowing() {
BigDumper threadDumper = mock(BigDumper.class);
CommandDispatcher dispatcher = new CommandDispatcherImpl(threadDumper);

assertDoesNotThrow(() -> dispatcher.dispatch("text/plain", "thread.dump\njob-123\n0\n1000"));
assertDoesNotThrow(() -> dispatcher.dispatch("text/plain", "thread.dump\njob-123\n101\n1000"));
assertDoesNotThrow(() -> dispatcher.dispatch("text/plain", "thread.dump\njob-123\n1\n300001"));
assertDoesNotThrow(
() -> dispatcher.dispatch("text/plain", "thread.dump\njob-123\n1\nnot-a-number"));
assertDoesNotThrow(() -> dispatcher.dispatch("thread.dump\njob-123\n0\n1000"));
assertDoesNotThrow(() -> dispatcher.dispatch("thread.dump\njob-123\n101\n1000"));
assertDoesNotThrow(() -> dispatcher.dispatch("thread.dump\njob-123\n1\n300001"));
assertDoesNotThrow(() -> dispatcher.dispatch("thread.dump\njob-123\n1\nnot-a-number"));

verifyNoInteractions(threadDumper);
}
Expand All @@ -90,6 +89,6 @@ void containsThreadDumpFailures() {
.thenThrow(new IllegalStateException("export failed"));
CommandDispatcher dispatcher = new CommandDispatcherImpl(threadDumper);

assertDoesNotThrow(() -> dispatcher.dispatch("text/plain", "thread.dump\njob-123"));
assertDoesNotThrow(() -> dispatcher.dispatch("thread.dump\njob-123"));
}
}
2 changes: 1 addition & 1 deletion dependencyManagement/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ val otelVersion = "1.64.0"
val otelAlphaVersion = otelVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1")
val otelInstrumentationVersion = "2.30.0"
val otelInstrumentationAlphaVersion = otelInstrumentationVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1")
val otelContribAlphaVersion = "1.58.0-alpha"
val otelContribAlphaVersion = "1.59.0-alpha"

val autoValueVersion = "1.11.1"
val dockerJavaVersion = "3.7.1"
Expand Down
12 changes: 6 additions & 6 deletions licenses/licenses.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# splunk-otel-javaagent
## Dependency License Report
_2026-07-23 08:15:00 PDT_
_2026-07-30 15:18:51 PDT_
## Apache License, Version 2.0

**1** **Group:** `com.squareup.okhttp3` **Name:** `okhttp` **Version:** `5.4.0`
Expand All @@ -20,11 +20,11 @@ _2026-07-23 08:15:00 PDT_
> - **POM Project URL**: [https://github.com/square/okio/](https://github.com/square/okio/)
> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)

**5** **Group:** `com.squareup.wire` **Name:** `wire-runtime` **Version:** `6.4.0`
**5** **Group:** `com.squareup.wire` **Name:** `wire-runtime` **Version:** `6.4.5`
> - **POM Project URL**: [https://github.com/square/wire/](https://github.com/square/wire/)
> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)

**6** **Group:** `com.squareup.wire` **Name:** `wire-runtime-jvm` **Version:** `6.4.0`
**6** **Group:** `com.squareup.wire` **Name:** `wire-runtime-jvm` **Version:** `6.4.5`
> - **POM Project URL**: [https://github.com/square/wire/](https://github.com/square/wire/)
> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)

Expand Down Expand Up @@ -76,15 +76,15 @@ _2026-07-23 08:15:00 PDT_
> - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java)
> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)

**19** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-opamp-client` **Version:** `1.58.0-alpha`
**19** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-opamp-client` **Version:** `1.59.0-alpha`
> - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib)
> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)

**20** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-resource-providers` **Version:** `1.58.0-alpha`
**20** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-resource-providers` **Version:** `1.59.0-alpha`
> - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib)
> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)

**21** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-samplers` **Version:** `1.58.0-alpha`
**21** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-samplers` **Version:** `1.59.0-alpha`
> - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib)
> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,14 @@ static OpampClient startOpampClient(
OpampClientBuilder builder = OpampClient.builder();
builder.enableEffectiveConfigReporting();

// Remote control uses OpAMP remote configuration as its transport, so advertise the
// remote configuration capabilities when either feature is enabled.
if (opampClientConfiguration.isRemoteConfigurationEnabled()
|| opampClientConfiguration.isRemoteControlAllowed()) {
if (opampClientConfiguration.isRemoteConfigurationEnabled()) {
builder.enableRemoteConfig();
}

if (opampClientConfiguration.isRemoteControlAllowed()) {
builder.addCustomCapability(ServerToAgentMessageHandler.CMD_CAPABILITY);
}

String endpoint = opampClientConfiguration.getEndpoint();
long pollingDurationMillis = opampClientConfiguration.getPollingInterval();
if (endpoint != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@
import com.splunk.opamp.remotecontrol.CommandDispatcher;
import io.opentelemetry.opamp.client.OpampClient;
import io.opentelemetry.opamp.client.internal.response.MessageData;
import opamp.proto.AgentConfigFile;
import opamp.proto.AgentRemoteConfig;
import opamp.proto.CustomMessage;

public class ServerToAgentMessageHandler {
public static final String MAGIC_CMD_STRING = "COMMAND_HACKS";
public static final String CMD_CAPABILITY = "com.splunk.opamp.experimental_command/v1";
public static final String CMD_TYPE = "command";
private final RemoteConfigProcessor remoteConfigProcessor;
private final CommandDispatcher commandDispatcher;

Expand All @@ -38,18 +39,14 @@ public class ServerToAgentMessageHandler {
public void handleMessage(MessageData message, OpampClient opampClient) {
AgentRemoteConfig remoteConfig = message.getRemoteConfig();
if (remoteConfig != null) {

if (remoteConfig.config.config_map.containsKey(MAGIC_CMD_STRING)) {
AgentConfigFile agentConfigFile = remoteConfig.config.config_map.get(MAGIC_CMD_STRING);
String contentType = agentConfigFile.content_type;
String body = agentConfigFile.body.string(UTF_8);
commandDispatcher.dispatch(contentType, body);
if (remoteConfig.config.config_map.size() == 1) { // just this command
return;
}
}

remoteConfigProcessor.applyConfig(remoteConfig, opampClient);
}
CustomMessage customMessage = message.getCustomMessage();
if (customMessage != null
&& CMD_CAPABILITY.equals(customMessage.capability)
&& CMD_TYPE.equals(customMessage.type)) {
String body = customMessage.data.string(UTF_8);
commandDispatcher.dispatch(body);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,16 @@
import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension;
import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.RecordedRequest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import okio.ByteString;
import opamp.proto.AgentCapabilities;
import opamp.proto.AgentConfigFile;
import opamp.proto.AgentConfigMap;
import opamp.proto.AgentRemoteConfig;
import opamp.proto.AgentToServer;
import opamp.proto.AnyValue;
import opamp.proto.ArrayValue;
import opamp.proto.CustomMessage;
import opamp.proto.KeyValue;
import opamp.proto.ServerErrorResponse;
import opamp.proto.ServerToAgent;
Expand Down Expand Up @@ -119,15 +115,14 @@ void testOpamp() throws Exception {
.put(AttributeKey.booleanArrayKey("boolobjarr"), Arrays.asList(true, true, false, true))
.build();
Resource resource = Resource.create(attributes);
Map<String, AgentConfigFile> configMap =
Collections.singletonMap(
"test-key",
new AgentConfigFile.Builder().body(ByteString.encodeUtf8("test-value")).build());
String commandBody = "thread.dump\njob-123\n3\n250";
ServerToAgent response =
new ServerToAgent.Builder()
.remote_config(
new AgentRemoteConfig.Builder()
.config(new AgentConfigMap.Builder().config_map(configMap).build())
.custom_message(
new CustomMessage.Builder()
.capability(ServerToAgentMessageHandler.CMD_CAPABILITY)
.type(ServerToAgentMessageHandler.CMD_TYPE)
.data(ByteString.encodeUtf8(commandBody))
.build())
.build();
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.X_PROTOBUF, response.encode()));
Expand All @@ -141,6 +136,7 @@ void testOpamp() throws Exception {
.withEndpoint(server.httpUri().toString())
.withPollingInterval(500)
.withRemoteConfigurationEnabled(true)
.withRemoteControlAllowed(true)
.build();
OpampClient client =
OpampActivator.startOpampClient(
Expand Down Expand Up @@ -172,17 +168,21 @@ public void onMessage(OpampClient opampClient, MessageData messageData) {

// when
MessageData message = result.get(5, TimeUnit.SECONDS);
AgentRemoteConfig remoteConfig = message.getRemoteConfig();
CustomMessage customMessage = message.getCustomMessage();

// then
assertThat(remoteConfig).isNotNull();
assertThat(remoteConfig.config.config_map.get("test-key").body.utf8()).isEqualTo("test-value");
assertThat(customMessage).isNotNull();
assertThat(customMessage.capability).isEqualTo(ServerToAgentMessageHandler.CMD_CAPABILITY);
assertThat(customMessage.type).isEqualTo(ServerToAgentMessageHandler.CMD_TYPE);
assertThat(customMessage.data.utf8()).isEqualTo(commandBody);

RecordedRequest recordedRequest = server.takeRequest();
byte[] body = recordedRequest.request().content().array();
AgentToServer agentToServer = AgentToServer.ADAPTER.decode(body);

assertRemoteConfigCapabilities(agentToServer, true);
assertThat(agentToServer.custom_capabilities.capabilities)
.containsExactly(ServerToAgentMessageHandler.CMD_CAPABILITY);
assertIdentifyingString(agentToServer, SERVICE_NAME, "test-service");
assertIdentifyingString(agentToServer, SERVICE_INSTANCE_ID, "test-instance");
assertIdentifyingString(agentToServer, SERVICE_NAMESPACE, "test-ns");
Expand Down Expand Up @@ -266,17 +266,28 @@ public void onMessage(OpampClient opampClient, MessageData messageData) {
}

@Test
void shouldAdvertiseRemoteConfigCapabilitiesWhenRemoteControlIsAllowed() throws Exception {
void shouldAdvertiseCustomCapabilityWhenRemoteControlIsAllowed() throws Exception {
AgentToServer agentToServer = startClientAndTakeInitialRequest(false, true);

assertRemoteConfigCapabilities(agentToServer, false);
assertThat(agentToServer.custom_capabilities.capabilities)
.containsExactly(ServerToAgentMessageHandler.CMD_CAPABILITY);
}

@Test
void shouldAdvertiseRemoteConfigCapabilitiesOnlyWhenRemoteConfigIsEnabled() throws Exception {
AgentToServer agentToServer = startClientAndTakeInitialRequest(true, false);

assertRemoteConfigCapabilities(agentToServer, true);
assertThat(agentToServer.custom_capabilities).isNull();
}

@Test
void shouldNotAdvertiseRemoteConfigCapabilitiesWhenRemoteFeaturesAreDisabled() throws Exception {
AgentToServer agentToServer = startClientAndTakeInitialRequest(false, false);

assertRemoteConfigCapabilities(agentToServer, false);
assertThat(agentToServer.custom_capabilities).isNull();
}

private AgentToServer startClientAndTakeInitialRequest(
Expand Down
Loading
Loading