From 331c0820e82eccb565f2edccb2a0ef6a71aca224 Mon Sep 17 00:00:00 2001 From: Kiril Keranov <114745615+kiril-keranov@users.noreply.github.com> Date: Tue, 10 Feb 2026 16:14:49 +0200 Subject: [PATCH 01/21] Refactor debug framework to parse yaml config --- src/java/frameworks/debug.go | 101 +++++++++++++++++------------------ 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 837432aa9..dbee301bd 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -3,6 +3,7 @@ package frameworks import ( "fmt" "github.com/cloudfoundry/java-buildpack/src/java/common" + "github.com/cloudfoundry/libbuildpack" "os" "strconv" ) @@ -21,23 +22,32 @@ func NewDebugFramework(ctx *common.Context) *DebugFramework { // Detect checks if debugging should be enabled func (d *DebugFramework) Detect() (string, error) { // Check if debug is enabled in configuration - enabled := d.isEnabled() - if !enabled { + config, err := d.loadConfig() + if err != nil { + d.context.Log.Warning("Failed to load debug config: %s", err.Error()) + return "", nil // Don't fail the build + } + if !config.isEnabled() { return "", nil } - port := d.getPort() + port := config.getPort() return fmt.Sprintf("debug=%d", port), nil } // Supply performs debug setup during supply phase func (d *DebugFramework) Supply() error { - if !d.isEnabled() { + config, err := d.loadConfig() + if err != nil { + d.context.Log.Warning("Failed to load debug config: %s", err.Error()) + return nil // Don't fail the build + } + if !config.isEnabled() { return nil } - port := d.getPort() - suspend := d.getSuspend() + port := config.getPort() + suspend := config.getSuspend() suspendMsg := "" if suspend { @@ -50,12 +60,17 @@ func (d *DebugFramework) Supply() error { // Finalize adds debug options to JAVA_OPTS func (d *DebugFramework) Finalize() error { - if !d.isEnabled() { + config, err := d.loadConfig() + if err != nil { + d.context.Log.Warning("Failed to load debug config: %s", err.Error()) + return nil // Don't fail the build + } + if !config.isEnabled() { return nil } - port := d.getPort() - suspend := d.getSuspend() + port := config.getPort() + suspend := config.getSuspend() // Build JDWP agent string suspendValue := "n" @@ -74,7 +89,7 @@ func (d *DebugFramework) Finalize() error { } // isEnabled checks if debugging is enabled -func (d *DebugFramework) isEnabled() bool { +func (d *debugConfig) isEnabled() bool { // Check BPL_DEBUG_ENABLED first (Cloud Native Buildpacks convention) bplEnabled := os.Getenv("BPL_DEBUG_ENABLED") if bplEnabled == "true" || bplEnabled == "1" { @@ -84,28 +99,11 @@ func (d *DebugFramework) isEnabled() bool { return false } - // Check JBP_CONFIG_DEBUG environment variable (Java Buildpack convention) - config := os.Getenv("JBP_CONFIG_DEBUG") - - // Parse the config to check for enabled: true - // For simplicity, if JBP_CONFIG_DEBUG is set and contains "enabled", check its value - // A more robust implementation would parse YAML - if config != "" { - // Simple check: if it contains "enabled: true" or just "true" - if contains(config, "enabled: true") || contains(config, "'enabled': true") { - return true - } - if contains(config, "enabled: false") || contains(config, "'enabled': false") { - return false - } - } - - // Default to disabled (matches Ruby buildpack default) - return false + return d.Enabled } // getPort returns the debug port -func (d *DebugFramework) getPort() int { +func (d *debugConfig) getPort() int { // Check BPL_DEBUG_PORT first (Cloud Native Buildpacks convention) bplPort := os.Getenv("BPL_DEBUG_PORT") if bplPort != "" { @@ -114,35 +112,36 @@ func (d *DebugFramework) getPort() int { } } - // Check JBP_CONFIG_DEBUG for port setting (Java Buildpack convention) - config := os.Getenv("JBP_CONFIG_DEBUG") - if config != "" { - // Simple parsing - look for port: XXXX - // A more robust implementation would parse YAML - if idx := findInString(config, "port:"); idx != -1 { - portStr := extractNumber(config[idx:]) - if port, err := strconv.Atoi(portStr); err == nil && port > 0 { - return port - } - } - } - - // Default port - return 8000 + return d.Port } // getSuspend returns whether to suspend on start -func (d *DebugFramework) getSuspend() bool { - // Check JBP_CONFIG_DEBUG for suspend setting +func (d *debugConfig) getSuspend() bool { + return d.Suspend +} + +type debugConfig struct { + Enabled bool `yaml:"enabled"` + Port int `yaml:"port"` + Suspend bool `yaml:"suspend"` +} + +var defaultConfig = debugConfig{ + Enabled: false, + Port: 8000, + Suspend: false, +} + +func (d *DebugFramework) loadConfig() (*debugConfig, error) { config := os.Getenv("JBP_CONFIG_DEBUG") if config != "" { - if contains(config, "suspend: true") || contains(config, "'suspend': true") { - return true + var jbpConfig debugConfig + if err := libbuildpack.NewYAML().Load(config, &jbpConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_DEBUG: %w", err) } + return &jbpConfig, nil } - - // Default to false - return false + return &defaultConfig, nil } // Helper function to check if string contains substring From 8420be78c12dc58ce7558c4e0a7f8f643fc71e56 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Tue, 10 Feb 2026 17:09:38 +0200 Subject: [PATCH 02/21] Add YamlHandler thin wrapper --- src/java/common/context.go | 17 +++++++++++++++-- src/java/frameworks/debug.go | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/java/common/context.go b/src/java/common/context.go index 92e20c44c..305a5e129 100644 --- a/src/java/common/context.go +++ b/src/java/common/context.go @@ -3,12 +3,12 @@ package common import ( "encoding/json" "fmt" + "github.com/cloudfoundry/libbuildpack" + "go.yaml.in/yaml/v3" "os" "path/filepath" "strconv" "strings" - - "github.com/cloudfoundry/libbuildpack" ) // Context holds shared dependencies for buildpack components @@ -199,3 +199,16 @@ func (s *VCAPService) HasTag(tag string) bool { func ContainsIgnoreCase(s, substr string) bool { return strings.Contains(strings.ToLower(s), strings.ToLower(substr)) } + +// YamlHandler provides a thin wrapper around yaml.v3's Marshal and Unmarshal. +type YamlHandler struct{} + +// Unmarshal decodes the YAML data into the provided destination. +func (h YamlHandler) Unmarshal(data []byte, out any) error { + return yaml.Unmarshal(data, out) +} + +// Marshal encodes the given value into YAML. +func (h YamlHandler) Marshal(in any) ([]byte, error) { + return yaml.Marshal(in) +} diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index dbee301bd..37b592755 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -3,7 +3,6 @@ package frameworks import ( "fmt" "github.com/cloudfoundry/java-buildpack/src/java/common" - "github.com/cloudfoundry/libbuildpack" "os" "strconv" ) @@ -136,7 +135,8 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { config := os.Getenv("JBP_CONFIG_DEBUG") if config != "" { var jbpConfig debugConfig - if err := libbuildpack.NewYAML().Load(config, &jbpConfig); err != nil { + yamlHandler := common.YamlHandler{} + if err := yamlHandler.Unmarshal([]byte(config), &jbpConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_DEBUG: %w", err) } return &jbpConfig, nil From 3d11f1f8cc87e40ae99581eb6ba4d11749682f5b Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Tue, 10 Feb 2026 17:29:05 +0200 Subject: [PATCH 03/21] Overlay JBP_CONFIG over default values --- src/java/frameworks/debug.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 37b592755..718699341 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -125,23 +125,22 @@ type debugConfig struct { Suspend bool `yaml:"suspend"` } -var defaultConfig = debugConfig{ - Enabled: false, - Port: 8000, - Suspend: false, -} - func (d *DebugFramework) loadConfig() (*debugConfig, error) { + // initialize default values + dbgConfig := &debugConfig{ + Enabled: false, + Port: 8000, + Suspend: false, + } config := os.Getenv("JBP_CONFIG_DEBUG") if config != "" { - var jbpConfig debugConfig yamlHandler := common.YamlHandler{} - if err := yamlHandler.Unmarshal([]byte(config), &jbpConfig); err != nil { + // overlay JBP_CONFIG_DEBUG over default values + if err := yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_DEBUG: %w", err) } - return &jbpConfig, nil } - return &defaultConfig, nil + return dbgConfig, nil } // Helper function to check if string contains substring From 34338f1f2ede13a0e3598277c9294083b6d0c839 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Tue, 10 Feb 2026 18:12:31 +0200 Subject: [PATCH 04/21] Try validating fields --- src/java/frameworks/debug.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 718699341..905d4dbff 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -3,8 +3,10 @@ package frameworks import ( "fmt" "github.com/cloudfoundry/java-buildpack/src/java/common" + "go.yaml.in/yaml/v3" "os" "strconv" + "strings" ) // DebugFramework implements Java remote debugging support @@ -133,6 +135,7 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { Suspend: false, } config := os.Getenv("JBP_CONFIG_DEBUG") + validateFields(config, dbgConfig) if config != "" { yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_DEBUG over default values @@ -143,6 +146,16 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { return dbgConfig, nil } +func validateFields(data string, cfg interface{}) { + dec := yaml.NewDecoder(strings.NewReader(data)) + dec.KnownFields(true) + + if err := dec.Decode(&cfg); err != nil { + fmt.Println("YAML error:", err) + return + } +} + // Helper function to check if string contains substring func contains(s, substr string) bool { return findInString(s, substr) != -1 From 350f3c80485082979189af52834b0bda69bc904b Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Tue, 10 Feb 2026 18:31:24 +0200 Subject: [PATCH 05/21] Try warning on field validation --- src/java/frameworks/debug.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 905d4dbff..860a5ab37 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -135,7 +135,10 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { Suspend: false, } config := os.Getenv("JBP_CONFIG_DEBUG") - validateFields(config, dbgConfig) + err := validateFields(config, dbgConfig) + if err != nil { + d.context.Log.Warning("Unknown user config values: %w", err) + } if config != "" { yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_DEBUG over default values @@ -146,14 +149,15 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { return dbgConfig, nil } -func validateFields(data string, cfg interface{}) { +func validateFields(data string, cfg *debugConfig) error { dec := yaml.NewDecoder(strings.NewReader(data)) dec.KnownFields(true) if err := dec.Decode(&cfg); err != nil { - fmt.Println("YAML error:", err) - return + return err } + + return nil } // Helper function to check if string contains substring From da6996f294129e6407ebdc3ef8cade27ac6617f9 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 11 Feb 2026 09:33:19 +0200 Subject: [PATCH 06/21] Adjust print err --- src/java/frameworks/debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 860a5ab37..83d64ea22 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -137,7 +137,7 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { config := os.Getenv("JBP_CONFIG_DEBUG") err := validateFields(config, dbgConfig) if err != nil { - d.context.Log.Warning("Unknown user config values: %w", err) + d.context.Log.Warning("Unknown user config values: %w", err.Error()) } if config != "" { yamlHandler := common.YamlHandler{} From e72ec557b41b402c5880f870e67f5705e70264cf Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 11 Feb 2026 15:29:37 +0200 Subject: [PATCH 07/21] Refactor frameworks that have enabled config option --- .../frameworks/client_certificate_mapper.go | 43 ++++++---- .../frameworks/container_security_provider.go | 61 +++++++------- src/java/frameworks/debug.go | 2 +- src/java/frameworks/jmx.go | 81 ++++++++++--------- src/java/frameworks/metric_writer.go | 39 +++++---- 5 files changed, 122 insertions(+), 104 deletions(-) diff --git a/src/java/frameworks/client_certificate_mapper.go b/src/java/frameworks/client_certificate_mapper.go index 52a06afb4..141862432 100644 --- a/src/java/frameworks/client_certificate_mapper.go +++ b/src/java/frameworks/client_certificate_mapper.go @@ -1,8 +1,8 @@ package frameworks import ( - "github.com/cloudfoundry/java-buildpack/src/java/common" "fmt" + "github.com/cloudfoundry/java-buildpack/src/java/common" "os" "path/filepath" ) @@ -23,7 +23,13 @@ func NewClientCertificateMapperFramework(ctx *common.Context) *ClientCertificate // Enabled by default to support mTLS scenarios, can be disabled via configuration func (c *ClientCertificateMapperFramework) Detect() (string, error) { // Check if explicitly disabled via configuration - if !c.isEnabled() { + config, err := c.loadConfig() + if err != nil { + c.context.Log.Warning("Failed to load ccm config: %s", err.Error()) + return "", nil // Don't fail the build + } + + if !config.isEnabled() { return "", nil } @@ -77,25 +83,28 @@ func (c *ClientCertificateMapperFramework) Finalize() error { return nil } -// isEnabled checks if client certificate mapper is enabled -// Default is true (enabled) to support mTLS scenarios unless explicitly disabled -func (c *ClientCertificateMapperFramework) isEnabled() bool { - // Check JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER environment variable +func (c *ClientCertificateMapperFramework) loadConfig() (*ccmConfig, error) { + // initialize default values + dbgConfig := &ccmConfig{ + Enabled: true, + } config := os.Getenv("JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER") - // Parse the config to check for enabled: false - // For simplicity, if JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER is set and contains "enabled", check its value - // A more robust implementation would parse YAML if config != "" { - // Simple check: if it contains "enabled: false" or "'enabled': false" - if contains(config, "enabled: false") || contains(config, "'enabled': false") { - return false - } - if contains(config, "enabled: true") || contains(config, "'enabled': true") { - return true + yamlHandler := common.YamlHandler{} + // overlay JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER over default values + if err := yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER: %w", err) } } + return dbgConfig, nil +} - // Default to enabled (to support mTLS client certificate authentication) - return true +type ccmConfig struct { + Enabled bool `yaml:"enabled"` +} + +// isEnabled checks if client certificate mapper is enabled +func (c *ccmConfig) isEnabled() bool { + return c.Enabled } diff --git a/src/java/frameworks/container_security_provider.go b/src/java/frameworks/container_security_provider.go index e12685eec..f40891522 100644 --- a/src/java/frameworks/container_security_provider.go +++ b/src/java/frameworks/container_security_provider.go @@ -95,13 +95,17 @@ func (c *ContainerSecurityProviderFramework) Finalize() error { return fmt.Errorf("failed to write security properties: %w", err) } + config, err := c.loadConfig() + if err != nil { + c.context.Log.Warning("Failed to load container security provider config: %s", err.Error()) + } // Add key manager and trust manager configuration if specified - keyManagerEnabled := c.getKeyManagerEnabled() + keyManagerEnabled := config.getKeyManagerEnabled() if keyManagerEnabled != "" { javaOpts += fmt.Sprintf(" -Dorg.cloudfoundry.security.keymanager.enabled=%s", keyManagerEnabled) } - trustManagerEnabled := c.getTrustManagerEnabled() + trustManagerEnabled := config.getTrustManagerEnabled() if trustManagerEnabled != "" { javaOpts += fmt.Sprintf(" -Dorg.cloudfoundry.security.trustmanager.enabled=%s", trustManagerEnabled) } @@ -214,44 +218,35 @@ func (c *ContainerSecurityProviderFramework) getDefaultSecurityProviders() []str } } -// getKeyManagerEnabled returns the key_manager_enabled configuration value -func (c *ContainerSecurityProviderFramework) getKeyManagerEnabled() string { - config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER") - if config == "" { - return "" +func (c *ContainerSecurityProviderFramework) loadConfig() (*cspConfig, error) { + // initialize default values + secConfig := &cspConfig{ + KeyManagerEnabled: "", + TrustManagerEnabled: "", } + config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER") - // Parse configuration for key_manager_enabled - // Format: {key_manager_enabled: true} or {'key_manager_enabled': 'true'} - if contains(config, "key_manager_enabled") { - if contains(config, "true") { - return "true" - } - if contains(config, "false") { - return "false" + if config != "" { + yamlHandler := common.YamlHandler{} + // overlay JBP_CONFIG_CONTAINER_SECURITY_PROVIDER over default values + if err := yamlHandler.Unmarshal([]byte(config), &secConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_CONTAINER_SECURITY_PROVIDER: %w", err) } } + return secConfig, nil +} - return "" +// getKeyManagerEnabled returns the key_manager_enabled configuration value +func (c *cspConfig) getKeyManagerEnabled() string { + return c.KeyManagerEnabled } // getTrustManagerEnabled returns the trust_manager_enabled configuration value -func (c *ContainerSecurityProviderFramework) getTrustManagerEnabled() string { - config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER") - if config == "" { - return "" - } - - // Parse configuration for trust_manager_enabled - // Format: {trust_manager_enabled: true} or {'trust_manager_enabled': 'true'} - if contains(config, "trust_manager_enabled") { - if contains(config, "true") { - return "true" - } - if contains(config, "false") { - return "false" - } - } +func (c *cspConfig) getTrustManagerEnabled() string { + return c.TrustManagerEnabled +} - return "" +type cspConfig struct { + KeyManagerEnabled string `yaml:"key_manager_enabled"` + TrustManagerEnabled string `yaml:"trust_manager_enabled"` } diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 83d64ea22..9bdbb2e2f 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -137,7 +137,7 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { config := os.Getenv("JBP_CONFIG_DEBUG") err := validateFields(config, dbgConfig) if err != nil { - d.context.Log.Warning("Unknown user config values: %w", err.Error()) + d.context.Log.Warning("Unknown user config values: %s", err.Error()) } if config != "" { yamlHandler := common.YamlHandler{} diff --git a/src/java/frameworks/jmx.go b/src/java/frameworks/jmx.go index 96e29b630..4671d12a9 100644 --- a/src/java/frameworks/jmx.go +++ b/src/java/frameworks/jmx.go @@ -21,33 +21,41 @@ func NewJmxFramework(ctx *common.Context) *JmxFramework { // Detect checks if JMX should be enabled func (j *JmxFramework) Detect() (string, error) { // Check if JMX is enabled in configuration - enabled := j.isEnabled() - if !enabled { + config, err := j.loadConfig() + if err != nil { + j.context.Log.Warning("Failed to load debug config: %s", err.Error()) + return "", nil // Don't fail the build + } + if !config.isEnabled() { return "", nil } - port := j.getPort() + port := config.getPort() return fmt.Sprintf("jmx=%d", port), nil } // Supply performs JMX setup during supply phase func (j *JmxFramework) Supply() error { - if !j.isEnabled() { - return nil + config, err := j.loadConfig() + if err != nil { + j.context.Log.Warning("Failed to load debug config: %s", err.Error()) + return nil // Don't fail the build } - port := j.getPort() + port := config.getPort() j.context.Log.BeginStep("JMX enabled on port %d", port) return nil } // Finalize adds JMX options to JAVA_OPTS via profile.d script func (j *JmxFramework) Finalize() error { - if !j.isEnabled() { - return nil + config, err := j.loadConfig() + if err != nil { + j.context.Log.Warning("Failed to load debug config: %s", err.Error()) + return nil // Don't fail the build } - port := j.getPort() + port := config.getPort() // Build JMX system properties jmxOpts := fmt.Sprintf( @@ -67,8 +75,26 @@ func (j *JmxFramework) Finalize() error { return nil } +func (j *JmxFramework) loadConfig() (*jmxConfig, error) { + // initialize default values + jConfig := &jmxConfig{ + Enabled: false, + Port: 5000, + } + config := os.Getenv("JBP_CONFIG_JMX") + + if config != "" { + yamlHandler := common.YamlHandler{} + // overlay JBP_CONFIG_JMX over default values + if err := yamlHandler.Unmarshal([]byte(config), &jConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_JMX: %w", err) + } + } + return jConfig, nil +} + // isEnabled checks if JMX is enabled -func (j *JmxFramework) isEnabled() bool { +func (j *jmxConfig) isEnabled() bool { // Check BPL_JMX_ENABLED first (Cloud Native Buildpacks convention) bplEnabled := os.Getenv("BPL_JMX_ENABLED") if bplEnabled == "true" || bplEnabled == "1" { @@ -79,24 +105,11 @@ func (j *JmxFramework) isEnabled() bool { } // Check JBP_CONFIG_JMX environment variable (Java Buildpack convention) - config := os.Getenv("JBP_CONFIG_JMX") - - // Parse the config to check for enabled: true - if config != "" { - if contains(config, "enabled: true") || contains(config, "'enabled': true") { - return true - } - if contains(config, "enabled: false") || contains(config, "'enabled': false") { - return false - } - } - - // Default to disabled (matches Ruby buildpack default) - return false + return j.Enabled } // getPort returns the JMX port -func (j *JmxFramework) getPort() int { +func (j *jmxConfig) getPort() int { // Check BPL_JMX_PORT first (Cloud Native Buildpacks convention) bplPort := os.Getenv("BPL_JMX_PORT") if bplPort != "" { @@ -105,18 +118,10 @@ func (j *JmxFramework) getPort() int { } } - // Check JBP_CONFIG_JMX for port setting (Java Buildpack convention) - config := os.Getenv("JBP_CONFIG_JMX") - if config != "" { - // Simple parsing - look for port: XXXX - if idx := findInString(config, "port:"); idx != -1 { - portStr := extractNumber(config[idx:]) - if port, err := strconv.Atoi(portStr); err == nil && port > 0 { - return port - } - } - } + return j.Port +} - // Default port - return 5000 +type jmxConfig struct { + Enabled bool `yaml:"enabled"` + Port int `yaml:"port"` } diff --git a/src/java/frameworks/metric_writer.go b/src/java/frameworks/metric_writer.go index f4590614e..394f163ce 100644 --- a/src/java/frameworks/metric_writer.go +++ b/src/java/frameworks/metric_writer.go @@ -23,7 +23,12 @@ func NewMetricWriterFramework(ctx *common.Context) *MetricWriterFramework { // Detects Micrometer presence and checks if enabled func (m *MetricWriterFramework) Detect() (string, error) { // Check if explicitly enabled via configuration - if !m.isEnabled() { + config, err := m.loadConfig() + if err != nil { + m.context.Log.Warning("Failed to load debug config: %s", err.Error()) + return "", nil // Don't fail the build + } + if !config.isEnabled() { m.context.Log.Debug("Metric Writer is disabled (default)") return "", nil } @@ -167,24 +172,28 @@ func (m *MetricWriterFramework) buildCFTagEnvVars() string { return strings.Join(envVars, "\n") } -// isEnabled checks if Metric Writer is enabled -// Default is false (disabled) unless explicitly enabled via configuration -func (m *MetricWriterFramework) isEnabled() bool { - // Check JBP_CONFIG_METRIC_WRITER environment variable +func (m *MetricWriterFramework) loadConfig() (*metricWriterConfig, error) { + // initialize default values + mwConfig := &metricWriterConfig{ + Enabled: true, + } config := os.Getenv("JBP_CONFIG_METRIC_WRITER") - // Parse the config to check for enabled: true if config != "" { - // Simple check: if it contains "enabled: true" or "'enabled': true" - if contains(config, "enabled: true") || contains(config, "'enabled': true") || - contains(config, "enabled : true") || contains(config, "'enabled' : true") { - return true - } - if contains(config, "enabled: false") || contains(config, "'enabled': false") { - return false + yamlHandler := common.YamlHandler{} + // overlay JBP_CONFIG_METRIC_WRITER over default values + if err := yamlHandler.Unmarshal([]byte(config), &mwConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_METRIC_WRITER: %w", err) } } + return mwConfig, nil +} - // Default to disabled - return false +type metricWriterConfig struct { + Enabled bool `yaml:"enabled"` +} + +// isEnabled checks if client certificate mapper is enabled +func (m *metricWriterConfig) isEnabled() bool { + return m.Enabled } From 30c0cc031257537215fd694d3b513f8082c8a0e1 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 11 Feb 2026 17:17:53 +0200 Subject: [PATCH 08/21] Adjust luna framework + refactoring --- src/java/common/context.go | 14 ---- src/java/common/yaml_handler.go | 26 ++++++++ src/java/frameworks/debug.go | 21 ++---- src/java/frameworks/luna_security_provider.go | 65 +++++++++++++------ 4 files changed, 74 insertions(+), 52 deletions(-) create mode 100644 src/java/common/yaml_handler.go diff --git a/src/java/common/context.go b/src/java/common/context.go index 305a5e129..da735415d 100644 --- a/src/java/common/context.go +++ b/src/java/common/context.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "github.com/cloudfoundry/libbuildpack" - "go.yaml.in/yaml/v3" "os" "path/filepath" "strconv" @@ -199,16 +198,3 @@ func (s *VCAPService) HasTag(tag string) bool { func ContainsIgnoreCase(s, substr string) bool { return strings.Contains(strings.ToLower(s), strings.ToLower(substr)) } - -// YamlHandler provides a thin wrapper around yaml.v3's Marshal and Unmarshal. -type YamlHandler struct{} - -// Unmarshal decodes the YAML data into the provided destination. -func (h YamlHandler) Unmarshal(data []byte, out any) error { - return yaml.Unmarshal(data, out) -} - -// Marshal encodes the given value into YAML. -func (h YamlHandler) Marshal(in any) ([]byte, error) { - return yaml.Marshal(in) -} diff --git a/src/java/common/yaml_handler.go b/src/java/common/yaml_handler.go new file mode 100644 index 000000000..b85c81483 --- /dev/null +++ b/src/java/common/yaml_handler.go @@ -0,0 +1,26 @@ +package common + +import ( + "bytes" + "go.yaml.in/yaml/v3" +) + +// YamlHandler provides a thin wrapper around yaml.v3's Marshal and Unmarshal. +type YamlHandler struct{} + +// Unmarshal decodes the YAML data into the provided destination. +func (h YamlHandler) Unmarshal(data []byte, out any) error { + return yaml.Unmarshal(data, out) +} + +// Marshal encodes the given value into YAML. +func (h YamlHandler) Marshal(in any) ([]byte, error) { + return yaml.Marshal(in) +} + +// ValidateFields is used to detect unknown fields during parsing of JBP_CONFIG* configurations +func (h YamlHandler) ValidateFields(data []byte, out interface{}) error { + dec := yaml.NewDecoder(bytes.NewReader(data)) + dec.KnownFields(true) + return dec.Decode(out) +} diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 9bdbb2e2f..8cacbf2de 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -3,10 +3,8 @@ package frameworks import ( "fmt" "github.com/cloudfoundry/java-buildpack/src/java/common" - "go.yaml.in/yaml/v3" "os" "strconv" - "strings" ) // DebugFramework implements Java remote debugging support @@ -129,35 +127,24 @@ type debugConfig struct { func (d *DebugFramework) loadConfig() (*debugConfig, error) { // initialize default values - dbgConfig := &debugConfig{ + dbgConfig := debugConfig{ Enabled: false, Port: 8000, Suspend: false, } config := os.Getenv("JBP_CONFIG_DEBUG") - err := validateFields(config, dbgConfig) + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), dbgConfig) if err != nil { d.context.Log.Warning("Unknown user config values: %s", err.Error()) } if config != "" { - yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_DEBUG over default values if err := yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_DEBUG: %w", err) } } - return dbgConfig, nil -} - -func validateFields(data string, cfg *debugConfig) error { - dec := yaml.NewDecoder(strings.NewReader(data)) - dec.KnownFields(true) - - if err := dec.Decode(&cfg); err != nil { - return err - } - - return nil + return &dbgConfig, nil } // Helper function to check if string contains substring diff --git a/src/java/frameworks/luna_security_provider.go b/src/java/frameworks/luna_security_provider.go index 6de8dc1d6..48b99f0c5 100644 --- a/src/java/frameworks/luna_security_provider.go +++ b/src/java/frameworks/luna_security_provider.go @@ -274,8 +274,14 @@ func (l *LunaSecurityProviderFramework) writeConfiguration(servers []interface{} } defer file.Close() + config, err := l.loadConfig() + if err != nil { + l.context.Log.Warning("Failed to load luna security provider config: %s", err.Error()) + return err + } + // Write prologue (library configuration and client settings) - if err := l.writePrologue(file); err != nil { + if err := l.writePrologue(file, config); err != nil { return err } @@ -298,7 +304,7 @@ func (l *LunaSecurityProviderFramework) writeConfiguration(servers []interface{} } // Write epilogue (HA configuration) - if err := l.writeEpilogue(file, groups); err != nil { + if err := l.writeEpilogue(file, groups, config); err != nil { return err } @@ -306,13 +312,13 @@ func (l *LunaSecurityProviderFramework) writeConfiguration(servers []interface{} } // writePrologue writes library configuration and client settings -func (l *LunaSecurityProviderFramework) writePrologue(file *os.File) error { +func (l *LunaSecurityProviderFramework) writePrologue(file *os.File, config *lunaSecurityProviderConfig) error { lunaDir := filepath.Join(l.context.Stager.DepDir(), "luna_security_provider") // Get configuration values - loggingEnabled := l.getConfigBool("logging_enabled", false) + loggingEnabled := config.getLoggingEnabled() tcpKeepAlive := 0 - if l.getConfigBool("tcp_keep_alive_enabled", false) { + if config.getTCPKeepAliveEnabled() { tcpKeepAlive = 1 } @@ -396,8 +402,8 @@ func (l *LunaSecurityProviderFramework) writeGroup(file *os.File, index int, gro } // writeEpilogue writes HA configuration and HASynchronize sections -func (l *LunaSecurityProviderFramework) writeEpilogue(file *os.File, groups []interface{}) error { - haLoggingEnabled := l.getConfigBool("ha_logging_enabled", true) +func (l *LunaSecurityProviderFramework) writeEpilogue(file *os.File, groups []interface{}, config *lunaSecurityProviderConfig) error { + haLoggingEnabled := config.getHALoggingEnabled() file.WriteString("}\n\n") file.WriteString("HAConfiguration = {\n") @@ -443,27 +449,44 @@ func (l *LunaSecurityProviderFramework) createSymlink(target, link string) error return os.Symlink(relTarget, link) } -// getConfigBool retrieves a boolean configuration value from JBP_CONFIG_LUNA_SECURITY_PROVIDER -func (l *LunaSecurityProviderFramework) getConfigBool(key string, defaultValue bool) bool { - config := os.Getenv("JBP_CONFIG_LUNA_SECURITY_PROVIDER") - if config == "" { - return defaultValue +func (l *LunaSecurityProviderFramework) loadConfig() (*lunaSecurityProviderConfig, error) { + // initialize default values + lspConfig := &lunaSecurityProviderConfig{ + HALoggingEnabled: true, + LoggingEnabled: false, + TCPKeepAliveEnabled: false, } + config := os.Getenv("JBP_CONFIG_LUNA_SECURITY_PROVIDER") - // Parse configuration for key - if contains(config, key) { - if contains(config, "true") { - return true - } - if contains(config, "false") { - return false + if config != "" { + yamlHandler := common.YamlHandler{} + // overlay JBP_CONFIG_LUNA_SECURITY_PROVIDER over default values + if err := yamlHandler.Unmarshal([]byte(config), &lspConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_LUNA_SECURITY_PROVIDER: %w", err) } } - - return defaultValue + return lspConfig, nil } // paddedIndex returns a zero-padded two-digit index string func (l *LunaSecurityProviderFramework) paddedIndex(index int) string { return fmt.Sprintf("%02d", index) } + +func (l *lunaSecurityProviderConfig) getHALoggingEnabled() bool { + return l.HALoggingEnabled +} + +func (l *lunaSecurityProviderConfig) getLoggingEnabled() bool { + return l.LoggingEnabled +} + +func (l *lunaSecurityProviderConfig) getTCPKeepAliveEnabled() bool { + return l.TCPKeepAliveEnabled +} + +type lunaSecurityProviderConfig struct { + HALoggingEnabled bool `yaml:"ha_logging_enabled"` + LoggingEnabled bool `yaml:"logging_enabled"` + TCPKeepAliveEnabled bool `yaml:"tcp_keep_alive_enabled"` +} From fc9e7f98ae3d659b62ad6c57f3291f03bbe72b48 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 11 Feb 2026 17:22:10 +0200 Subject: [PATCH 09/21] Fix issue --- src/java/frameworks/debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 8cacbf2de..1cb313547 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -134,7 +134,7 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { } config := os.Getenv("JBP_CONFIG_DEBUG") yamlHandler := common.YamlHandler{} - err := yamlHandler.ValidateFields([]byte(config), dbgConfig) + err := yamlHandler.ValidateFields([]byte(config), &dbgConfig) if err != nil { d.context.Log.Warning("Unknown user config values: %s", err.Error()) } From 93be3ddba8377d43ab52956186bb595bf6e736f8 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 11 Feb 2026 17:58:32 +0200 Subject: [PATCH 10/21] Add validations + refactoring --- src/java/frameworks/client_certificate_mapper.go | 13 ++++++++----- src/java/frameworks/container_security_provider.go | 11 +++++++---- src/java/frameworks/jmx.go | 11 +++++++---- src/java/frameworks/luna_security_provider.go | 11 +++++++---- src/java/frameworks/metric_writer.go | 11 +++++++---- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/java/frameworks/client_certificate_mapper.go b/src/java/frameworks/client_certificate_mapper.go index 141862432..81cb98693 100644 --- a/src/java/frameworks/client_certificate_mapper.go +++ b/src/java/frameworks/client_certificate_mapper.go @@ -85,19 +85,22 @@ func (c *ClientCertificateMapperFramework) Finalize() error { func (c *ClientCertificateMapperFramework) loadConfig() (*ccmConfig, error) { // initialize default values - dbgConfig := &ccmConfig{ + mapperConfig := ccmConfig{ Enabled: true, } config := os.Getenv("JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER") - + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &mapperConfig) + if err != nil { + c.context.Log.Warning("Unknown user config values: %s", err.Error()) + } if config != "" { - yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER over default values - if err := yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil { + if err := yamlHandler.Unmarshal([]byte(config), &mapperConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER: %w", err) } } - return dbgConfig, nil + return &mapperConfig, nil } type ccmConfig struct { diff --git a/src/java/frameworks/container_security_provider.go b/src/java/frameworks/container_security_provider.go index f40891522..ae1a63f64 100644 --- a/src/java/frameworks/container_security_provider.go +++ b/src/java/frameworks/container_security_provider.go @@ -220,20 +220,23 @@ func (c *ContainerSecurityProviderFramework) getDefaultSecurityProviders() []str func (c *ContainerSecurityProviderFramework) loadConfig() (*cspConfig, error) { // initialize default values - secConfig := &cspConfig{ + secConfig := cspConfig{ KeyManagerEnabled: "", TrustManagerEnabled: "", } config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER") - + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &secConfig) + if err != nil { + c.context.Log.Warning("Unknown user config values: %s", err.Error()) + } if config != "" { - yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_CONTAINER_SECURITY_PROVIDER over default values if err := yamlHandler.Unmarshal([]byte(config), &secConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_CONTAINER_SECURITY_PROVIDER: %w", err) } } - return secConfig, nil + return &secConfig, nil } // getKeyManagerEnabled returns the key_manager_enabled configuration value diff --git a/src/java/frameworks/jmx.go b/src/java/frameworks/jmx.go index 4671d12a9..80c4a8120 100644 --- a/src/java/frameworks/jmx.go +++ b/src/java/frameworks/jmx.go @@ -77,20 +77,23 @@ func (j *JmxFramework) Finalize() error { func (j *JmxFramework) loadConfig() (*jmxConfig, error) { // initialize default values - jConfig := &jmxConfig{ + jConfig := jmxConfig{ Enabled: false, Port: 5000, } config := os.Getenv("JBP_CONFIG_JMX") - + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &jConfig) + if err != nil { + j.context.Log.Warning("Unknown user config values: %s", err.Error()) + } if config != "" { - yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_JMX over default values if err := yamlHandler.Unmarshal([]byte(config), &jConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_JMX: %w", err) } } - return jConfig, nil + return &jConfig, nil } // isEnabled checks if JMX is enabled diff --git a/src/java/frameworks/luna_security_provider.go b/src/java/frameworks/luna_security_provider.go index 48b99f0c5..4f41ec357 100644 --- a/src/java/frameworks/luna_security_provider.go +++ b/src/java/frameworks/luna_security_provider.go @@ -451,21 +451,24 @@ func (l *LunaSecurityProviderFramework) createSymlink(target, link string) error func (l *LunaSecurityProviderFramework) loadConfig() (*lunaSecurityProviderConfig, error) { // initialize default values - lspConfig := &lunaSecurityProviderConfig{ + lspConfig := lunaSecurityProviderConfig{ HALoggingEnabled: true, LoggingEnabled: false, TCPKeepAliveEnabled: false, } config := os.Getenv("JBP_CONFIG_LUNA_SECURITY_PROVIDER") - + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &lspConfig) + if err != nil { + l.context.Log.Warning("Unknown user config values: %s", err.Error()) + } if config != "" { - yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_LUNA_SECURITY_PROVIDER over default values if err := yamlHandler.Unmarshal([]byte(config), &lspConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_LUNA_SECURITY_PROVIDER: %w", err) } } - return lspConfig, nil + return &lspConfig, nil } // paddedIndex returns a zero-padded two-digit index string diff --git a/src/java/frameworks/metric_writer.go b/src/java/frameworks/metric_writer.go index 394f163ce..0e37ffa89 100644 --- a/src/java/frameworks/metric_writer.go +++ b/src/java/frameworks/metric_writer.go @@ -174,19 +174,22 @@ func (m *MetricWriterFramework) buildCFTagEnvVars() string { func (m *MetricWriterFramework) loadConfig() (*metricWriterConfig, error) { // initialize default values - mwConfig := &metricWriterConfig{ + mwConfig := metricWriterConfig{ Enabled: true, } config := os.Getenv("JBP_CONFIG_METRIC_WRITER") - + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &mwConfig) + if err != nil { + m.context.Log.Warning("Unknown user config values: %s", err.Error()) + } if config != "" { - yamlHandler := common.YamlHandler{} // overlay JBP_CONFIG_METRIC_WRITER over default values if err := yamlHandler.Unmarshal([]byte(config), &mwConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_METRIC_WRITER: %w", err) } } - return mwConfig, nil + return &mwConfig, nil } type metricWriterConfig struct { From 2d1d045e12827e7d191fa57845b57e7a0b47bde8 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 11 Feb 2026 18:26:31 +0200 Subject: [PATCH 11/21] Adjust validation --- src/java/frameworks/client_certificate_mapper.go | 12 ++++++------ src/java/frameworks/container_security_provider.go | 12 ++++++------ src/java/frameworks/debug.go | 12 ++++++------ src/java/frameworks/jmx.go | 12 ++++++------ src/java/frameworks/luna_security_provider.go | 12 ++++++------ src/java/frameworks/metric_writer.go | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/java/frameworks/client_certificate_mapper.go b/src/java/frameworks/client_certificate_mapper.go index 81cb98693..85c097183 100644 --- a/src/java/frameworks/client_certificate_mapper.go +++ b/src/java/frameworks/client_certificate_mapper.go @@ -89,14 +89,14 @@ func (c *ClientCertificateMapperFramework) loadConfig() (*ccmConfig, error) { Enabled: true, } config := os.Getenv("JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER") - yamlHandler := common.YamlHandler{} - err := yamlHandler.ValidateFields([]byte(config), &mapperConfig) - if err != nil { - c.context.Log.Warning("Unknown user config values: %s", err.Error()) - } if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &mapperConfig) + if err != nil { + c.context.Log.Warning("Unknown user config values: %s", err.Error()) + } // overlay JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER over default values - if err := yamlHandler.Unmarshal([]byte(config), &mapperConfig); err != nil { + if err = yamlHandler.Unmarshal([]byte(config), &mapperConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER: %w", err) } } diff --git a/src/java/frameworks/container_security_provider.go b/src/java/frameworks/container_security_provider.go index ae1a63f64..3c060784e 100644 --- a/src/java/frameworks/container_security_provider.go +++ b/src/java/frameworks/container_security_provider.go @@ -225,14 +225,14 @@ func (c *ContainerSecurityProviderFramework) loadConfig() (*cspConfig, error) { TrustManagerEnabled: "", } config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER") - yamlHandler := common.YamlHandler{} - err := yamlHandler.ValidateFields([]byte(config), &secConfig) - if err != nil { - c.context.Log.Warning("Unknown user config values: %s", err.Error()) - } if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &secConfig) + if err != nil { + c.context.Log.Warning("Unknown user config values: %s", err.Error()) + } // overlay JBP_CONFIG_CONTAINER_SECURITY_PROVIDER over default values - if err := yamlHandler.Unmarshal([]byte(config), &secConfig); err != nil { + if err = yamlHandler.Unmarshal([]byte(config), &secConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_CONTAINER_SECURITY_PROVIDER: %w", err) } } diff --git a/src/java/frameworks/debug.go b/src/java/frameworks/debug.go index 1cb313547..01af41076 100644 --- a/src/java/frameworks/debug.go +++ b/src/java/frameworks/debug.go @@ -133,14 +133,14 @@ func (d *DebugFramework) loadConfig() (*debugConfig, error) { Suspend: false, } config := os.Getenv("JBP_CONFIG_DEBUG") - yamlHandler := common.YamlHandler{} - err := yamlHandler.ValidateFields([]byte(config), &dbgConfig) - if err != nil { - d.context.Log.Warning("Unknown user config values: %s", err.Error()) - } if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &dbgConfig) + if err != nil { + d.context.Log.Warning("Unknown user config values: %s", err.Error()) + } // overlay JBP_CONFIG_DEBUG over default values - if err := yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil { + if err = yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_DEBUG: %w", err) } } diff --git a/src/java/frameworks/jmx.go b/src/java/frameworks/jmx.go index 80c4a8120..0414e8928 100644 --- a/src/java/frameworks/jmx.go +++ b/src/java/frameworks/jmx.go @@ -82,14 +82,14 @@ func (j *JmxFramework) loadConfig() (*jmxConfig, error) { Port: 5000, } config := os.Getenv("JBP_CONFIG_JMX") - yamlHandler := common.YamlHandler{} - err := yamlHandler.ValidateFields([]byte(config), &jConfig) - if err != nil { - j.context.Log.Warning("Unknown user config values: %s", err.Error()) - } if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &jConfig) + if err != nil { + j.context.Log.Warning("Unknown user config values: %s", err.Error()) + } // overlay JBP_CONFIG_JMX over default values - if err := yamlHandler.Unmarshal([]byte(config), &jConfig); err != nil { + if err = yamlHandler.Unmarshal([]byte(config), &jConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_JMX: %w", err) } } diff --git a/src/java/frameworks/luna_security_provider.go b/src/java/frameworks/luna_security_provider.go index 4f41ec357..7524a40c7 100644 --- a/src/java/frameworks/luna_security_provider.go +++ b/src/java/frameworks/luna_security_provider.go @@ -457,14 +457,14 @@ func (l *LunaSecurityProviderFramework) loadConfig() (*lunaSecurityProviderConfi TCPKeepAliveEnabled: false, } config := os.Getenv("JBP_CONFIG_LUNA_SECURITY_PROVIDER") - yamlHandler := common.YamlHandler{} - err := yamlHandler.ValidateFields([]byte(config), &lspConfig) - if err != nil { - l.context.Log.Warning("Unknown user config values: %s", err.Error()) - } if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &lspConfig) + if err != nil { + l.context.Log.Warning("Unknown user config values: %s", err.Error()) + } // overlay JBP_CONFIG_LUNA_SECURITY_PROVIDER over default values - if err := yamlHandler.Unmarshal([]byte(config), &lspConfig); err != nil { + if err = yamlHandler.Unmarshal([]byte(config), &lspConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_LUNA_SECURITY_PROVIDER: %w", err) } } diff --git a/src/java/frameworks/metric_writer.go b/src/java/frameworks/metric_writer.go index 0e37ffa89..627a7ea71 100644 --- a/src/java/frameworks/metric_writer.go +++ b/src/java/frameworks/metric_writer.go @@ -178,14 +178,14 @@ func (m *MetricWriterFramework) loadConfig() (*metricWriterConfig, error) { Enabled: true, } config := os.Getenv("JBP_CONFIG_METRIC_WRITER") - yamlHandler := common.YamlHandler{} - err := yamlHandler.ValidateFields([]byte(config), &mwConfig) - if err != nil { - m.context.Log.Warning("Unknown user config values: %s", err.Error()) - } if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &mwConfig) + if err != nil { + m.context.Log.Warning("Unknown user config values: %s", err.Error()) + } // overlay JBP_CONFIG_METRIC_WRITER over default values - if err := yamlHandler.Unmarshal([]byte(config), &mwConfig); err != nil { + if err = yamlHandler.Unmarshal([]byte(config), &mwConfig); err != nil { return nil, fmt.Errorf("failed to parse JBP_CONFIG_METRIC_WRITER: %w", err) } } From fd1b8d31d82516f9d9849672730d40c768baf1cd Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Thu, 12 Feb 2026 15:09:40 +0200 Subject: [PATCH 12/21] Adjust jrebel and jma frameworks with the missing props --- src/java/frameworks/java_memory_assistant.go | 126 ++++++++++++++----- src/java/frameworks/jrebel_agent.go | 39 ++++++ 2 files changed, 137 insertions(+), 28 deletions(-) diff --git a/src/java/frameworks/java_memory_assistant.go b/src/java/frameworks/java_memory_assistant.go index 5e2f53f60..c52103e23 100644 --- a/src/java/frameworks/java_memory_assistant.go +++ b/src/java/frameworks/java_memory_assistant.go @@ -23,7 +23,12 @@ func NewJavaMemoryAssistantFramework(ctx *common.Context) *JavaMemoryAssistantFr // Must be explicitly enabled via configuration (disabled by default) func (j *JavaMemoryAssistantFramework) Detect() (string, error) { // Check if explicitly enabled via configuration - if !j.isEnabled() { + config, err := j.loadConfig() + if err != nil { + j.context.Log.Warning("Failed to load java memory assistant config: %s", err.Error()) + return "", nil // Don't fail the build + } + if !config.isEnabled() { j.context.Log.Debug("Java Memory Assistant is disabled (default)") return "", nil } @@ -115,10 +120,11 @@ func (j *JavaMemoryAssistantFramework) buildAgentConfig() string { var configParts []string // Get configuration from JBP_CONFIG_JAVA_MEMORY_ASSISTANT environment variable - config := os.Getenv("JBP_CONFIG_JAVA_MEMORY_ASSISTANT") - - // Parse configuration (simplified - in production, parse YAML properly) - // For now, we'll use default values that can be overridden + config, err := j.loadConfig() + if err != nil { + j.context.Log.Warning("Failed to load java memory assistant config: %s", err.Error()) + return "" // Don't fail the build + } // Heap dump folder (default: $PWD or volume service mount point) heapDumpFolder := j.getHeapDumpFolder() @@ -127,25 +133,25 @@ func (j *JavaMemoryAssistantFramework) buildAgentConfig() string { } // Check interval (default: 5s) - checkInterval := j.getConfigValue(config, "check_interval", "5s") + checkInterval := config.Agent.CheckInterval configParts = append(configParts, fmt.Sprintf("check-interval=%s", checkInterval)) // Max frequency (default: 1/1m) - maxFrequency := j.getConfigValue(config, "max_frequency", "1/1m") + maxFrequency := config.Agent.MaxFrequency configParts = append(configParts, fmt.Sprintf("max-frequency=%s", maxFrequency)) // Log level (use buildpack log level if not specified) - logLevel := j.getConfigValue(config, "log_level", "INFO") + logLevel := config.Agent.LogLevel configParts = append(configParts, fmt.Sprintf("log-level=%s", logLevel)) // Thresholds (default: old_gen >600MB) - thresholds := j.getThresholds(config) + thresholds := config.getThresholds() for memArea, threshold := range thresholds { configParts = append(configParts, fmt.Sprintf("threshold.%s=%s", memArea, threshold)) } // Max dump count (default: 1) - maxDumpCount := j.getConfigValue(config, "max_dump_count", "1") + maxDumpCount := config.CleanUp.MaxDumpCount configParts = append(configParts, fmt.Sprintf("max-dump-count=%s", maxDumpCount)) return strings.Join(configParts, ",") @@ -186,33 +192,97 @@ func (j *JavaMemoryAssistantFramework) getConfigValue(config, key, defaultValue return defaultValue } +func (j *JavaMemoryAssistantFramework) loadConfig() (*jmaConfig, error) { + // initialize default values + jConfig := jmaConfig{ + Enabled: false, + Agent: Agent{ + HeapDumpFolder: "", + CheckInterval: "5s", + MaxFrequency: "1/1m", + LogLevel: "", + Thresholds: Thresholds{ + Heap: "", + CodeCache: "", + Metaspace: "", + PermGen: "", + CompressedClass: "", + Eden: "", + Survivor: "", + OldGen: ">600MB", + TenuredGen: "", + CodeHeapNonNMethods: "", + CodeHeapNonProfiled: "", + CodeHeapProfiledNMethods: "", + }, + }, + CleanUp: CleanUp{ + MaxDumpCount: 1, + }, + } + config := os.Getenv("JBP_CONFIG_JAVA_MEMORY_ASSISTANT") + if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &jConfig) + if err != nil { + j.context.Log.Warning("Unknown user config values: %s", err.Error()) + } + // overlay JBP_CONFIG_JAVA_MEMORY_ASSISTANT over default values + if err = yamlHandler.Unmarshal([]byte(config), &jConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_JAVA_MEMORY_ASSISTANT: %w", err) + } + } + return &jConfig, nil +} + // getThresholds extracts memory threshold configuration -func (j *JavaMemoryAssistantFramework) getThresholds(config string) map[string]string { +func (j *jmaConfig) getThresholds() map[string]string { thresholds := make(map[string]string) - // Default threshold: old_gen >600MB - thresholds["old_gen"] = ">600MB" + yamlHandler := common.YamlHandler{} + data, _ := yamlHandler.Marshal(j.Agent.Thresholds) + + var result map[string]string + yamlHandler.Unmarshal(data, &result) - // In production, parse thresholds from config - // For now, use default return thresholds } // isEnabled checks if Java Memory Assistant is enabled // Default is false (disabled) unless explicitly enabled via configuration -func (j *JavaMemoryAssistantFramework) isEnabled() bool { - // Check JBP_CONFIG_JAVA_MEMORY_ASSISTANT environment variable - config := os.Getenv("JBP_CONFIG_JAVA_MEMORY_ASSISTANT") +func (j *jmaConfig) isEnabled() bool { + return j.Enabled +} - // Parse the config to check for enabled: true - if config != "" { - // Simple check: if it contains "enabled: true" or "'enabled': true" - if contains(config, "enabled: true") || contains(config, "'enabled': true") || - contains(config, "enabled : true") || contains(config, "'enabled' : true") { - return true - } - } +type jmaConfig struct { + Enabled bool `yaml:"enabled"` + Agent Agent `yaml:"agent"` + CleanUp CleanUp `yaml:"clean_up"` +} + +type Agent struct { + HeapDumpFolder string `yaml:"heap_dump_folder"` + CheckInterval string `yaml:"check_interval"` + MaxFrequency string `yaml:"max_frequency"` + LogLevel string `yaml:"log_level"` + Thresholds Thresholds `yaml:"thresholds"` +} + +type Thresholds struct { + Heap string `yaml:"heap"` + CodeCache string `yaml:"code_cache"` + Metaspace string `yaml:"metaspace"` + PermGen string `yaml:"perm_gen"` + CompressedClass string `yaml:"compressed_class"` + Eden string `yaml:"eden"` + Survivor string `yaml:"survivor"` + OldGen string `yaml:"old_gen"` + TenuredGen string `yaml:"tenured_gen"` + CodeHeapNonNMethods string `yaml:"code_heap.non_nmethods"` + CodeHeapNonProfiled string `yaml:"code_heap.non_profiled_nmethods"` + CodeHeapProfiledNMethods string `yaml:"code_heap.profiled_nmethods"` +} - // Default to disabled - return false +type CleanUp struct { + MaxDumpCount int `yaml:"max_dump_count"` } diff --git a/src/java/frameworks/jrebel_agent.go b/src/java/frameworks/jrebel_agent.go index 24352689e..e10af4fef 100644 --- a/src/java/frameworks/jrebel_agent.go +++ b/src/java/frameworks/jrebel_agent.go @@ -20,6 +20,16 @@ func NewJRebelAgentFramework(ctx *common.Context) *JRebelAgentFramework { // Detect determines if JRebel configuration exists in the application func (j *JRebelAgentFramework) Detect() (string, error) { + // Check if explicitly disabled via configuration + config, err := j.loadConfig() + if err != nil { + j.context.Log.Warning("Failed to load jrebel config: %s", err.Error()) + return "", nil // Don't fail the build + } + + if !config.isEnabled() { + return "", nil + } // Check for rebel-remote.xml configuration file in the app rebelRemoteXML := filepath.Join(j.context.Stager.BuildDir(), "rebel-remote.xml") if _, err := os.Stat(rebelRemoteXML); err == nil { @@ -118,3 +128,32 @@ func (j *JRebelAgentFramework) Finalize() error { j.context.Log.Info("JRebel Agent configured successfully (priority 31)") return nil } + +func (j *JRebelAgentFramework) loadConfig() (*jrebelConfig, error) { + // initialize default values + jrConfig := jrebelConfig{ + Enabled: true, + } + config := os.Getenv("JBP_CONFIG_JREBEL") + if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &jrConfig) + if err != nil { + j.context.Log.Warning("Unknown user config values: %s", err.Error()) + } + // overlay JBP_CONFIG_JREBEL over default values + if err = yamlHandler.Unmarshal([]byte(config), &jrConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_JREBEL: %w", err) + } + } + return &jrConfig, nil +} + +type jrebelConfig struct { + Enabled bool `yaml:"enabled"` +} + +// isEnabled checks if client certificate mapper is enabled +func (j *jrebelConfig) isEnabled() bool { + return j.Enabled +} From 637ec9b97188e960639663b1fadc3f420e4cc679 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Thu, 12 Feb 2026 17:24:25 +0200 Subject: [PATCH 13/21] Adjust google stack drive and sealights frameworks --- .../frameworks/google_stackdriver_profiler.go | 40 ++++++++++++++ src/java/frameworks/sealights_agent.go | 54 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/java/frameworks/google_stackdriver_profiler.go b/src/java/frameworks/google_stackdriver_profiler.go index ac1d1c166..9fe14635d 100644 --- a/src/java/frameworks/google_stackdriver_profiler.go +++ b/src/java/frameworks/google_stackdriver_profiler.go @@ -28,6 +28,7 @@ import ( type GoogleStackdriverProfilerFramework struct { context *common.Context agentPath string + config *googleStackDriveConfig } // NewGoogleStackdriverProfilerFramework creates a new Google Stackdriver Profiler framework instance @@ -125,6 +126,12 @@ func (g *GoogleStackdriverProfilerFramework) Finalize() error { agentArgs = append(agentArgs, fmt.Sprintf("-cprof_service=%s", appName)) } + err = g.loadConfig() + if err != nil { + g.context.Log.Warning("Failed to load google stack driver profiler config: %s", err.Error()) + return nil // Do not fail the build + } + // Add service version if appVersion := g.getApplicationVersion(); appVersion != "" { agentArgs = append(agentArgs, fmt.Sprintf("-cprof_service_version=%s", appVersion)) @@ -200,6 +207,9 @@ func (g *GoogleStackdriverProfilerFramework) getCredentials() GoogleProfilerCred // getApplicationName returns the application name func (g *GoogleStackdriverProfilerFramework) getApplicationName() string { + if g.config.ApplicationName != "" { + return g.config.ApplicationName + } vcapApp := os.Getenv("VCAP_APPLICATION") if vcapApp == "" { return "" @@ -219,6 +229,9 @@ func (g *GoogleStackdriverProfilerFramework) getApplicationName() string { // getApplicationVersion returns the application version func (g *GoogleStackdriverProfilerFramework) getApplicationVersion() string { + if g.config.ApplicationVersion != "" { + return g.config.ApplicationVersion + } vcapApp := os.Getenv("VCAP_APPLICATION") if vcapApp == "" { return "" @@ -245,3 +258,30 @@ func (g *GoogleStackdriverProfilerFramework) constructAgentPath(profilerDir stri g.agentPath = agentPattern return nil } + +func (g *GoogleStackdriverProfilerFramework) loadConfig() error { + // initialize default values + gsdConfig := googleStackDriveConfig{ + ApplicationName: "", + ApplicationVersion: "", + } + config := os.Getenv("JBP_CONFIG_GOOGLE_STACK_DRIVE_PROFILER") + if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &gsdConfig) + if err != nil { + g.context.Log.Warning("Unknown user config values: %s", err.Error()) + } + // overlay JBP_CONFIG_GOOGLE_STACK_DRIVE_PROFILER over default values + if err = yamlHandler.Unmarshal([]byte(config), &gsdConfig); err != nil { + return fmt.Errorf("failed to parse JBP_CONFIG_GOOGLE_STACK_DRIVE_PROFILER: %w", err) + } + } + g.config = &gsdConfig + return nil +} + +type googleStackDriveConfig struct { + ApplicationName string `yaml:"application_name"` + ApplicationVersion string `yaml:"application_version"` +} diff --git a/src/java/frameworks/sealights_agent.go b/src/java/frameworks/sealights_agent.go index 313b9ab21..18b360f0f 100644 --- a/src/java/frameworks/sealights_agent.go +++ b/src/java/frameworks/sealights_agent.go @@ -173,6 +173,30 @@ func (f *SealightsAgentFramework) Finalize() error { // Build javaagent argument javaAgent := fmt.Sprintf("-javaagent:%s", runtimeAgentPath) + // Add if custom config is at place + config, err := f.loadConfig() + if err != nil { + f.context.Log.Warning("Failed to load sealight config: %s", err.Error()) + return nil // Don't fail the build + } + if config.BuildSessionId != "" { + systemProps += fmt.Sprintf(" -Dsl.buildSessionId=%s", config.BuildSessionId) + } + if slProxy, ok := service.Credentials["sl.proxy"].(string); ok && slProxy != "" { + systemProps += fmt.Sprintf(" -Dsl.proxy=%s", slProxy) + } else { + if config.Proxy != "" { + systemProps += fmt.Sprintf(" -Dsl.proxy=%s", config.Proxy) + } + } + if slLabId, ok := service.Credentials["sl.labId"].(string); ok && slLabId != "" { + systemProps += fmt.Sprintf(" -Dsl.labId=%s", slLabId) + } else { + if config.LabId != "" { + systemProps += fmt.Sprintf(" -Dsl.labId=%s", config.LabId) + } + } + // Combine javaagent and system properties javaOpts := fmt.Sprintf("%s %s", javaAgent, systemProps) @@ -190,3 +214,33 @@ func (f *SealightsAgentFramework) Finalize() error { f.context.Log.Info("Sealights Agent configured (priority 39)") return nil } + +func (f *SealightsAgentFramework) loadConfig() (*sealightConfig, error) { + // initialize default values + sConfig := sealightConfig{ + BuildSessionId: "", + LabId: "", + Proxy: "", + AutoUpgrade: false, + } + config := os.Getenv("JBP_CONFIG_SEALIGHTS") + if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &sConfig) + if err != nil { + f.context.Log.Warning("Unknown user config values: %s", err.Error()) + } + // overlay JBP_CONFIG_SEALIGHTS over default values + if err = yamlHandler.Unmarshal([]byte(config), &sConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_SEALIGHTS: %w", err) + } + } + return &sConfig, nil +} + +type sealightConfig struct { + BuildSessionId string `yaml:"build_session_id"` + LabId string `yaml:"lab_id"` + Proxy string `yaml:"proxy"` + AutoUpgrade bool `yaml:"auto_upgrade"` +} From 4277f8f8c08a1bb2094ef46ad3a77d6a4164a012 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 10:37:09 +0200 Subject: [PATCH 14/21] Fix framework and tests --- src/integration/frameworks_test.go | 4 ++-- src/java/frameworks/google_stackdriver_profiler.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/integration/frameworks_test.go b/src/integration/frameworks_test.go index 639e70b82..0a2d83506 100644 --- a/src/integration/frameworks_test.go +++ b/src/integration/frameworks_test.go @@ -536,7 +536,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin deployment, logs, err := platform.Deploy. WithEnv(map[string]string{ "BP_JAVA_VERSION": "11", - "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{enabled: true}'", + "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{ enabled: true }'", }). Execute(name, filepath.Join(fixtures, "containers", "tomcat")) Expect(err).NotTo(HaveOccurred(), logs.String) @@ -550,7 +550,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin deployment, logs, err := platform.Deploy. WithEnv(map[string]string{ "BP_JAVA_VERSION": "11", - "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{enabled: false}'", + "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{ enabled: false }'", }). Execute(name, filepath.Join(fixtures, "containers", "tomcat")) Expect(err).NotTo(HaveOccurred(), logs.String) diff --git a/src/java/frameworks/google_stackdriver_profiler.go b/src/java/frameworks/google_stackdriver_profiler.go index 9fe14635d..176a45ed7 100644 --- a/src/java/frameworks/google_stackdriver_profiler.go +++ b/src/java/frameworks/google_stackdriver_profiler.go @@ -121,17 +121,17 @@ func (g *GoogleStackdriverProfilerFramework) Finalize() error { // Build agentpath option with arguments var agentArgs []string - // Add service name (application name) - if appName := g.getApplicationName(); appName != "" { - agentArgs = append(agentArgs, fmt.Sprintf("-cprof_service=%s", appName)) - } - err = g.loadConfig() if err != nil { g.context.Log.Warning("Failed to load google stack driver profiler config: %s", err.Error()) return nil // Do not fail the build } + // Add service name (application name) + if appName := g.getApplicationName(); appName != "" { + agentArgs = append(agentArgs, fmt.Sprintf("-cprof_service=%s", appName)) + } + // Add service version if appVersion := g.getApplicationVersion(); appVersion != "" { agentArgs = append(agentArgs, fmt.Sprintf("-cprof_service_version=%s", appVersion)) From 4d85b49e350fee2e57ef472f6accba5fc7359624 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 14:18:10 +0200 Subject: [PATCH 15/21] Add config and refactor jprofiler frmrk --- src/integration/frameworks_test.go | 6 +- src/java/frameworks/jprofiler_profiler.go | 68 ++++++++++++++++------- src/java/frameworks/sky_walking_agent.go | 39 ++++++++++++- 3 files changed, 89 insertions(+), 24 deletions(-) diff --git a/src/integration/frameworks_test.go b/src/integration/frameworks_test.go index 0a2d83506..7ce096e68 100644 --- a/src/integration/frameworks_test.go +++ b/src/integration/frameworks_test.go @@ -536,7 +536,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin deployment, logs, err := platform.Deploy. WithEnv(map[string]string{ "BP_JAVA_VERSION": "11", - "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{ enabled: true }'", + "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "{ enabled: true }", }). Execute(name, filepath.Join(fixtures, "containers", "tomcat")) Expect(err).NotTo(HaveOccurred(), logs.String) @@ -550,7 +550,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin deployment, logs, err := platform.Deploy. WithEnv(map[string]string{ "BP_JAVA_VERSION": "11", - "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{ enabled: false }'", + "JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "{ enabled: false }", }). Execute(name, filepath.Join(fixtures, "containers", "tomcat")) Expect(err).NotTo(HaveOccurred(), logs.String) @@ -851,7 +851,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin deployment, logs, err := platform.Deploy. WithEnv(map[string]string{ "BP_JAVA_VERSION": "11", - "JBP_CONFIG_JPROFILER_PROFILER": "'{enabled: true}'", + "JBP_CONFIG_JPROFILER_PROFILER": "{ enabled: true }", }). Execute(name, filepath.Join(fixtures, "containers", "spring_boot_staged")) Expect(err).NotTo(HaveOccurred(), logs.String) diff --git a/src/java/frameworks/jprofiler_profiler.go b/src/java/frameworks/jprofiler_profiler.go index 5526d63e9..8072c5066 100644 --- a/src/java/frameworks/jprofiler_profiler.go +++ b/src/java/frameworks/jprofiler_profiler.go @@ -38,13 +38,13 @@ func NewJProfilerProfilerFramework(ctx *common.Context) *JProfilerProfilerFramew func (f *JProfilerProfilerFramework) Detect() (string, error) { // JProfiler is disabled by default // Check for JBP_CONFIG_JPROFILER_PROFILER='{enabled: true}' - enabled := os.Getenv("JBP_CONFIG_JPROFILER_PROFILER") - if enabled != "" { - // Check if "enabled:true" in the agent options - // We need case-insensitive check due to inconsistent casing - if common.ContainsIgnoreCase(enabled, "enabled") && common.ContainsIgnoreCase(enabled, "true") { - return "JProfiler Profiler", nil - } + config, err := f.loadConfig() + if err != nil { + f.context.Log.Warning("Failed to load jprofile profiler config: %s", err.Error()) + return "", nil // Don't fail the build + } + if config.isEnabled() { + return "JProfiler Profiler", nil } return "", nil @@ -111,24 +111,20 @@ func (f *JProfilerProfilerFramework) Finalize() error { } runtimeAgentPath := filepath.Join(fmt.Sprintf("$DEPS_DIR/%s", depsIdx), relPath) - // Build agent options - // Default options: port=8849, nowait (don't wait for profiler UI to connect) - port := "8849" - portConfig := os.Getenv("JBP_CONFIG_JPROFILER_PROFILER") - if portConfig != "" && common.ContainsIgnoreCase(portConfig, "port") { - // Simple extraction (would need proper YAML parsing in production) - // For now, use default + config, err := f.loadConfig() + if err != nil { + f.context.Log.Warning("Failed to load jprofile profiler config: %s", err.Error()) + return nil // Don't fail the build } - // Check for nowait option (default: true) - nowait := "nowait" - if portConfig != "" && common.ContainsIgnoreCase(portConfig, "nowait") && common.ContainsIgnoreCase(portConfig, "false") { - nowait = "" - } + // Build agent options + // Default options: port=8849, nowait (don't wait for profiler UI to connect) + port := config.Port + nowait := config.NoWait // Build agent path with options var agentOptions string - if nowait != "" { + if nowait { agentOptions = fmt.Sprintf("port=%s,%s", port, nowait) } else { agentOptions = fmt.Sprintf("port=%s", port) @@ -143,3 +139,35 @@ func (f *JProfilerProfilerFramework) Finalize() error { f.context.Log.Info("JProfiler Profiler configured (priority 30)") return nil } + +func (f *JProfilerProfilerFramework) loadConfig() (*jProfilerConfig, error) { + // initialize default values + jpConfig := jProfilerConfig{ + Enabled: false, + NoWait: true, + Port: 8849, + } + config := os.Getenv("JBP_CONFIG_JPROFILER_PROFILER") + if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &jpConfig) + if err != nil { + f.context.Log.Warning("Unknown user config values: %s", err.Error()) + } + // overlay JBP_CONFIG_JPROFILER_PROFILER over default values + if err = yamlHandler.Unmarshal([]byte(config), &jpConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_JPROFILER_PROFILER: %w", err) + } + } + return &jpConfig, nil +} + +type jProfilerConfig struct { + Enabled bool `yaml:"enabled"` + NoWait bool `yaml:"nowait"` + Port int `yaml:"port"` +} + +func (j *jProfilerConfig) isEnabled() bool { + return j.Enabled +} diff --git a/src/java/frameworks/sky_walking_agent.go b/src/java/frameworks/sky_walking_agent.go index 9edb9b5e6..b70897fbf 100644 --- a/src/java/frameworks/sky_walking_agent.go +++ b/src/java/frameworks/sky_walking_agent.go @@ -120,7 +120,7 @@ func (s *SkyWalkingAgentFramework) Finalize() error { opts = append(opts, fmt.Sprintf("-javaagent:%s", runtimeJarPath)) // Configure application name (default to space:application_name) - appName := GetApplicationName(true) + appName := s.getAppName() if appName != "" { opts = append(opts, fmt.Sprintf("-Dskywalking.agent.service_name=%s", appName)) } @@ -200,6 +200,19 @@ func (s *SkyWalkingAgentFramework) getCredentials() SkyWalkingCredentials { return creds } +func (s *SkyWalkingAgentFramework) getAppName() string { + appName := GetApplicationName(true) + if appName != "" { + return appName + } + config, err := s.loadConfig() + if err != nil { + s.context.Log.Warning("Failed to load sky walking agent config: %s", err.Error()) + return "" + } + return config.DefaultApplicationName +} + func (s *SkyWalkingAgentFramework) constructJarPath(agentDir string) error { // Find the installed agent JAR (in skywalking-agent subdirectory) jarPattern := filepath.Join(agentDir, "skywalking-agent", "skywalking-agent.jar") @@ -209,3 +222,27 @@ func (s *SkyWalkingAgentFramework) constructJarPath(agentDir string) error { s.jarPath = jarPattern return nil } + +func (s *SkyWalkingAgentFramework) loadConfig() (*skyWalkingAgentConfig, error) { + // initialize default values + swaConfig := skyWalkingAgentConfig{ + DefaultApplicationName: "", + } + config := os.Getenv("JBP_CONFIG_SKY_WALKING_AGENT") + if config != "" { + yamlHandler := common.YamlHandler{} + err := yamlHandler.ValidateFields([]byte(config), &swaConfig) + if err != nil { + s.context.Log.Warning("Unknown user config values: %s", err.Error()) + } + // overlay JBP_CONFIG_SKY_WALKING_AGENT over default values + if err = yamlHandler.Unmarshal([]byte(config), &swaConfig); err != nil { + return nil, fmt.Errorf("failed to parse JBP_CONFIG_SKY_WALKING_AGENT: %w", err) + } + } + return &swaConfig, nil +} + +type skyWalkingAgentConfig struct { + DefaultApplicationName string `yaml:"default_application_name"` +} From edba3dcf50546908769f230bfe3313b2770570b4 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 14:23:21 +0200 Subject: [PATCH 16/21] Fix jprofiler agent options --- src/java/frameworks/jprofiler_profiler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java/frameworks/jprofiler_profiler.go b/src/java/frameworks/jprofiler_profiler.go index 8072c5066..64eb1b3f2 100644 --- a/src/java/frameworks/jprofiler_profiler.go +++ b/src/java/frameworks/jprofiler_profiler.go @@ -125,9 +125,9 @@ func (f *JProfilerProfilerFramework) Finalize() error { // Build agent path with options var agentOptions string if nowait { - agentOptions = fmt.Sprintf("port=%s,%s", port, nowait) + agentOptions = fmt.Sprintf("port=%v,%v", port, nowait) } else { - agentOptions = fmt.Sprintf("port=%s", port) + agentOptions = fmt.Sprintf("port=%v", port) } javaAgent := fmt.Sprintf("-agentpath:%s=%s", runtimeAgentPath, agentOptions) From b921f2d3d4d2dbc18e0cd978bff8ad390b538dce Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 14:50:50 +0200 Subject: [PATCH 17/21] Debug log --- src/java/frameworks/jprofiler_profiler.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/java/frameworks/jprofiler_profiler.go b/src/java/frameworks/jprofiler_profiler.go index 64eb1b3f2..bd473d064 100644 --- a/src/java/frameworks/jprofiler_profiler.go +++ b/src/java/frameworks/jprofiler_profiler.go @@ -122,6 +122,8 @@ func (f *JProfilerProfilerFramework) Finalize() error { port := config.Port nowait := config.NoWait + opts := fmt.Sprintf("port=%v,%v", port, nowait) + f.context.Log.Warning("jprofile profiler %s", opts) // Build agent path with options var agentOptions string if nowait { From a29983f093c981dd8383bb62a983b256cc30e8d8 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 14:59:03 +0200 Subject: [PATCH 18/21] Adjust info log --- src/java/frameworks/jprofiler_profiler.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/java/frameworks/jprofiler_profiler.go b/src/java/frameworks/jprofiler_profiler.go index bd473d064..66661ea1c 100644 --- a/src/java/frameworks/jprofiler_profiler.go +++ b/src/java/frameworks/jprofiler_profiler.go @@ -121,9 +121,7 @@ func (f *JProfilerProfilerFramework) Finalize() error { // Default options: port=8849, nowait (don't wait for profiler UI to connect) port := config.Port nowait := config.NoWait - - opts := fmt.Sprintf("port=%v,%v", port, nowait) - f.context.Log.Warning("jprofile profiler %s", opts) + // Build agent path with options var agentOptions string if nowait { @@ -133,6 +131,7 @@ func (f *JProfilerProfilerFramework) Finalize() error { } javaAgent := fmt.Sprintf("-agentpath:%s=%s", runtimeAgentPath, agentOptions) + f.context.Log.Info("JProfiler Profiler java agent options: %s", javaAgent) // Write to .opts file using priority 30 if err := writeJavaOptsFile(f.context, 30, "jprofiler_profiler", javaAgent); err != nil { return fmt.Errorf("failed to write java_opts file: %w", err) From 614d4ccec4647e7961bb46a4b725eb68175dd73d Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 15:07:21 +0200 Subject: [PATCH 19/21] Try fixing nowait --- src/java/frameworks/jprofiler_profiler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java/frameworks/jprofiler_profiler.go b/src/java/frameworks/jprofiler_profiler.go index 66661ea1c..9325149c1 100644 --- a/src/java/frameworks/jprofiler_profiler.go +++ b/src/java/frameworks/jprofiler_profiler.go @@ -121,11 +121,11 @@ func (f *JProfilerProfilerFramework) Finalize() error { // Default options: port=8849, nowait (don't wait for profiler UI to connect) port := config.Port nowait := config.NoWait - + // Build agent path with options var agentOptions string if nowait { - agentOptions = fmt.Sprintf("port=%v,%v", port, nowait) + agentOptions = fmt.Sprintf("port=%v,nowait=%v", port, nowait) } else { agentOptions = fmt.Sprintf("port=%v", port) } From 156bccb1a8ceb7d12ede3fb67c94aeae63fd549f Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 15:16:37 +0200 Subject: [PATCH 20/21] Fix nowait for jprofiler --- src/java/frameworks/jprofiler_profiler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java/frameworks/jprofiler_profiler.go b/src/java/frameworks/jprofiler_profiler.go index 9325149c1..43627e25b 100644 --- a/src/java/frameworks/jprofiler_profiler.go +++ b/src/java/frameworks/jprofiler_profiler.go @@ -121,11 +121,11 @@ func (f *JProfilerProfilerFramework) Finalize() error { // Default options: port=8849, nowait (don't wait for profiler UI to connect) port := config.Port nowait := config.NoWait - + // Build agent path with options var agentOptions string if nowait { - agentOptions = fmt.Sprintf("port=%v,nowait=%v", port, nowait) + agentOptions = fmt.Sprintf("port=%v,%v", port, "nowait") } else { agentOptions = fmt.Sprintf("port=%v", port) } From 8616ee19a141d2ef0509e5fb4995de5cc147d6ee Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Fri, 13 Feb 2026 15:47:00 +0200 Subject: [PATCH 21/21] Fix jma argument property --- src/java/frameworks/java_memory_assistant.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/frameworks/java_memory_assistant.go b/src/java/frameworks/java_memory_assistant.go index c52103e23..32fa10e02 100644 --- a/src/java/frameworks/java_memory_assistant.go +++ b/src/java/frameworks/java_memory_assistant.go @@ -152,7 +152,7 @@ func (j *JavaMemoryAssistantFramework) buildAgentConfig() string { // Max dump count (default: 1) maxDumpCount := config.CleanUp.MaxDumpCount - configParts = append(configParts, fmt.Sprintf("max-dump-count=%s", maxDumpCount)) + configParts = append(configParts, fmt.Sprintf("max-dump-count=%v", maxDumpCount)) return strings.Join(configParts, ",") }