Skip to content
Open
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
23 changes: 16 additions & 7 deletions docs/framework-jacoco_agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,22 @@ Users may optionally provide their own JaCoCo service. A user-provided JaCoCo se

The credential payload of the service may contain the following entries:

| Name | Description
| ---- | -----------
| `address` | The host for the agent to connect to or listen on
| `excludes` | (Optional) A list of class names that should be excluded from execution analysis. The list entries are separated by a colon (:) and may use wildcard characters (* and ?).
| `includes` | (Optional) A list of class names that should be included in execution analysis. The list entries are separated by a colon (:) and may use wildcard characters (* and ?).
| `port` | (Optional) The port for the agent to connect to or listen on
| `output` | (Optional) The mode for the agent. Possible values are either tcpclient (default) or tcpserver.
| Name | Description
|------------------------|------------
| `address` | The host for the agent to connect to or listen on.
| `destfile` | (Optional) The path to the file where execution data is written. Default is `jacoco.exec`.
| `sessionid` | (Optional) The identifier for the coverage session. Useful for distinguishing between different test runs.
| `append` | (Optional) If set to `true`, coverage data is appended to the existing file. Default is `false`.
| `includes` | (Optional) A list of class names to include in execution analysis. Entries are separated by a colon (`:`) and may use wildcards (`*`, `?`).
| `excludes` | (Optional) A list of class names to exclude from execution analysis. Entries are separated by a colon (`:`) and may use wildcards (`*`, `?`).
| `exclclassloader` | (Optional) A list of class loader names to exclude from instrumentation. Entries are separated by a colon (`:`).
| `inclbootstrapclasses` | (Optional) If set to `true`, includes bootstrap classes for instrumentation. Default is `false`.
| `inclnolocationclasses`| (Optional) If set to `true`, includes classes without a location for instrumentation. Default is `false`.
| `dumponexit` | (Optional) If set to `true`, coverage data is written on JVM shutdown. Default is `true`.
| `output` | (Optional) The mode for the agent. Possible values are `tcpclient` (default) or `tcpserver`.
| `port` | (Optional) The port for the agent to connect to or listen on.
| `classdumpdir` | (Optional) The directory where class files are dumped if class dumping is enabled.
| `jmx` | (Optional) If set to `true`, enables JMX control for the agent. Default is `false`.

## Configuration
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].
Expand Down
62 changes: 48 additions & 14 deletions lib/java_buildpack/framework/jacoco_agent.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

# Cloud Foundry Java Buildpack
# Copyright 2013-2020 the original author or authors.
# Copyright 2013-2025 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,19 +31,8 @@ def compile

# (see JavaBuildpack::Component::BaseComponent#release)
def release
credentials = @application.services.find_service(FILTER, ADDRESS)['credentials']
properties = {
'address' => credentials[ADDRESS],
'output' => 'tcpclient',
'sessionid' => '$CF_INSTANCE_GUID'
}

properties['excludes'] = credentials['excludes'] if credentials.key? 'excludes'
properties['includes'] = credentials['includes'] if credentials.key? 'includes'
properties['port'] = credentials['port'] if credentials.key? 'port'
properties['output'] = credentials['output'] if credentials.key? 'output'

@droplet.java_opts.add_javaagent_with_props(@droplet.sandbox + 'jacocoagent.jar', properties)
@droplet.java_opts.add_javaagent_with_props(@droplet.sandbox + 'jacocoagent.jar',
build_properties(service_credentials))
end

protected
Expand All @@ -59,6 +48,51 @@ def supports?

private_constant :ADDRESS, :FILTER

OPTIONAL_PROPS = {
'excludes' => nil,
'includes' => nil,
'port' => nil,
'destfile' => nil,
'append' => nil,
'exclclassloader' => nil,
'inclbootstrapclasses' => nil,
'inclnolocationclasses' => nil,
'dumponexit' => nil,
'classdumpdir' => nil,
'jmx' => nil
}.freeze

private_constant :ADDRESS, :FILTER, :OPTIONAL_PROPS

private

def service_credentials
@application.services.find_service(FILTER, ADDRESS)['credentials']
end

def build_properties(credentials)
properties = base_properties(credentials)
properties.merge!(optional_properties(credentials))
end

def base_properties(credentials)
{
'address' => credentials[ADDRESS],
'output' => credentials['output'] || 'tcpclient',
'sessionid' => credentials['sessionid'] || '$CF_INSTANCE_GUID'
}
end

def optional_properties(credentials)
opts = {}
OPTIONAL_PROPS.each_key do |key|
next unless credentials.key?(key)

opts[key] = credentials[key]
end
opts
end

end

end
Expand Down
44 changes: 43 additions & 1 deletion spec/java_buildpack/framework/jacoco_agent_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

# Cloud Foundry Java Buildpack
# Copyright 2013-2020 the original author or authors.
# Copyright 2013-2025 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,6 +33,42 @@
allow(services).to receive(:one_service?).with(/jacoco/, 'address').and_return(true)
end

let(:extended_credentials) do
{ 'address' => 'ext-address',
'output' => 'tcpserver',
'sessionid' => 'ext-session',
'excludes' => 'ex.*',
'includes' => 'in.*',
'port' => 7654,
'destfile' => 'custom.exec',
'append' => 'false',
'exclclassloader' => 'loader.*',
'inclbootstrapclasses' => 'true',
'inclnolocationclasses' => 'true',
'dumponexit' => 'false',
'classdumpdir' => 'dumpdir',
'jmx' => 'true' }
end

let(:extended_expected) do
[
'-javaagent:$PWD/.java-buildpack/jacoco_agent/jacocoagent.jar=address=ext-address',
'output=tcpserver',
'sessionid=ext-session',
'excludes=ex.*',
'includes=in.*',
'port=7654',
'destfile=custom.exec',
'append=false',
'exclclassloader=loader.*',
'inclbootstrapclasses=true',
'inclnolocationclasses=true',
'dumponexit=false',
'classdumpdir=dumpdir',
'jmx=true'
].join(',')
end

it 'detects with jacoco service' do
expect(component.detect).to eq("jacoco-agent=#{version}")
end
Expand Down Expand Up @@ -68,6 +104,12 @@
'excludes=test-excludes,includes=test-includes,port=6300')
end

it 'updates JAVA_OPTS with extended options' do
allow(services).to receive(:find_service).and_return('credentials' => extended_credentials)
component.release
expect(java_opts).to include(extended_expected)
end

end

end