@@ -6,18 +6,17 @@ import (
66 "net/http"
77 "os"
88 "path/filepath"
9- "regexp"
109 "strings"
1110
1211 "github.com/cloudfoundry/java-buildpack/src/java/common"
1312 "github.com/cloudfoundry/java-buildpack/src/java/resources"
1413 "github.com/cloudfoundry/libbuildpack"
15- yaml "gopkg.in/yaml.v2"
1614)
1715
1816// TomcatContainer handles servlet/WAR applications
1917type TomcatContainer struct {
2018 context * common.Context
19+ config * tomcatConfig
2120}
2221
2322// NewTomcatContainer creates a new Tomcat container
@@ -58,15 +57,21 @@ func (t *TomcatContainer) Supply() error {
5857 var dep libbuildpack.Dependency
5958 var err error
6059
60+ t .config , err = t .loadConfig ()
61+ if err != nil {
62+ return fmt .Errorf ("failed to load tomcat config: %w" , err )
63+ }
64+
6165 if javaHome != "" {
6266 javaMajorVersion , versionErr := common .DetermineJavaVersion (javaHome )
6367 if versionErr == nil {
64- tomcatVersion := determineTomcatVersion ( os . Getenv ( "JBP_CONFIG_TOMCAT" ) )
68+ tomcatVersion := DetermineTomcatVersion ( t . config . Tomcat . Version )
6569 t .context .Log .Debug ("Detected Java major version: %d" , javaMajorVersion )
6670
6771 // Select Tomcat version pattern based on Java version
6872 var versionPattern string
6973 if tomcatVersion == "" {
74+ t .context .Log .Info ("Tomcat version not specified" )
7075 if javaMajorVersion >= 11 {
7176 // Java 11+: Use Tomcat 10.x (Jakarta EE 9+)
7277 versionPattern = "10.x"
@@ -110,7 +115,7 @@ func (t *TomcatContainer) Supply() error {
110115
111116 // Install Tomcat with strip components to remove the top-level directory
112117 // Apache Tomcat tarballs extract to apache-tomcat-X.Y.Z/ subdirectory
113- tomcatDir := filepath . Join ( t . context . Stager . DepDir (), "tomcat" )
118+ tomcatDir := t . tomcatDir ( )
114119 if err := t .context .Installer .InstallDependencyWithStrip (dep , tomcatDir , 1 ); err != nil {
115120 return fmt .Errorf ("failed to install Tomcat: %w" , err )
116121 }
@@ -186,7 +191,7 @@ func (t *TomcatContainer) installTomcatLifecycleSupport() error {
186191
187192 // InstallDependency for JAR files (non-archives) copies the file to the target directory
188193 // The JAR will be placed in tomcat/lib/ as tomcat/lib/tomcat-lifecycle-support-X.Y.Z.RELEASE.jar
189- tomcatDir := filepath .Join (t .context . Stager . DepDir (), "tomcat" )
194+ tomcatDir := filepath .Join (t .tomcatDir () )
190195 libDir := filepath .Join (tomcatDir , "lib" )
191196
192197 // Ensure lib directory exists
@@ -211,7 +216,7 @@ func (t *TomcatContainer) installTomcatAccessLoggingSupport() error {
211216
212217 // InstallDependency for JAR files (non-archives) copies the file to the target directory
213218 // The JAR will be placed in tomcat/lib/ as tomcat/lib/tomcat-access-logging-support-X.Y.Z.RELEASE.jar
214- tomcatDir := filepath .Join (t .context . Stager . DepDir (), "tomcat" )
219+ tomcatDir := filepath .Join (t .tomcatDir () )
215220 libDir := filepath .Join (tomcatDir , "lib" )
216221
217222 // Ensure lib directory exists
@@ -238,7 +243,7 @@ func (t *TomcatContainer) installTomcatLoggingSupport() (string, error) {
238243
239244 // InstallDependency for JAR files (non-archives) copies the file to the target directory
240245 // The JAR will be placed in tomcat/bin/ as tomcat/bin/tomcat-logging-support-X.Y.Z.RELEASE.jar
241- tomcatDir := filepath .Join (t .context . Stager . DepDir (), "tomcat" )
246+ tomcatDir := filepath .Join (t .tomcatDir () )
242247 binDir := filepath .Join (tomcatDir , "bin" )
243248
244249 // Ensure bin directory exists
@@ -356,7 +361,8 @@ func (t *TomcatContainer) downloadExternalConfiguration(repositoryRoot, version,
356361
357362 // Parse YAML as map[string]string (version -> URL)
358363 var index map [string ]string
359- if err := yaml .Unmarshal (indexData , & index ); err != nil {
364+ yamlHandler := common.YamlHandler {}
365+ if err := yamlHandler .Unmarshal (indexData , & index ); err != nil {
360366 return fmt .Errorf ("failed to parse index.yml: %w" , err )
361367 }
362368
@@ -456,33 +462,15 @@ func getKeys(m map[string]string) []string {
456462 return keys
457463}
458464
459- // DetermineTomcatVersion is an exported wrapper around determineTomcatVersion.
460- // It exists primarily to allow unit tests in the containers_test package to
461- // verify Tomcat version parsing behavior without changing production semantics.
462- func DetermineTomcatVersion (raw string ) string {
463- return determineTomcatVersion (raw )
464- }
465-
466- // determineTomcatVersion determines the version of the tomcat
465+ // DetermineTomcatVersion determines the version of the tomcat
467466// based on the JBP_CONFIG_TOMCAT field from manifest.
468467// It looks for a tomcat block with a version of the form "<major>.+" (e.g. "9.+", "10.+", "10.1.+").
469468// Returns the pattern with "+" replaced by "*" (e.g. "9.*", "10.*", "10.1.*") so libbuildpack can resolve it.
470469// Masterminds/semver treats x, X, and * as equivalent wildcards.
471- func determineTomcatVersion (raw string ) string {
472- raw = strings .TrimSpace (raw )
473- if raw == "" {
474- return ""
475- }
476-
477- re := regexp .MustCompile (`(?i)tomcat\s*:\s*\{[\s\S]*?version\s*:\s*["']?([\d.]+\.\+)` )
478- match := re .FindStringSubmatch (raw )
479- if len (match ) < 2 {
480- return ""
481- }
482-
470+ func DetermineTomcatVersion (version string ) string {
483471 // Replace "+" with "*" so libbuildpack's FindMatchingVersion can resolve it.
484472 // e.g. "9.+" -> "9.*", "10.+" -> "10.*", "10.1.+" -> "10.1.*"
485- return strings .ReplaceAll (match [ 1 ] , "+" , "*" )
473+ return strings .ReplaceAll (version , "+" , "*" )
486474}
487475
488476// isAccessLoggingEnabled checks if access logging is enabled in configuration
@@ -491,108 +479,28 @@ func determineTomcatVersion(raw string) string {
491479// Can be enabled via: JBP_CONFIG_TOMCAT='{access_logging_support: {access_logging: enabled}}'
492480func (t * TomcatContainer ) isAccessLoggingEnabled () string {
493481 // Check for JBP_CONFIG_TOMCAT environment variable
494- configEnv := os .Getenv ("JBP_CONFIG_TOMCAT" )
495- if configEnv != "" {
496- t .context .Log .Debug ("Checking access logging configuration in JBP_CONFIG_TOMCAT" )
497-
498- // Look for access_logging_support section with access_logging: enabled
499- // Format: {access_logging_support: {access_logging: enabled}}
500- if strings .Contains (configEnv , "access_logging_support" ) {
501- // Check if access_logging is set to enabled
502- if strings .Contains (configEnv , "access_logging" ) &&
503- (strings .Contains (configEnv , "enabled" ) || strings .Contains (configEnv , "true" )) {
504- t .context .Log .Info ("Access logging enabled via JBP_CONFIG_TOMCAT" )
505- return "true"
506- }
507- // Check if explicitly disabled
508- if strings .Contains (configEnv , "access_logging" ) &&
509- (strings .Contains (configEnv , "disabled" ) || strings .Contains (configEnv , "false" )) {
510- t .context .Log .Debug ("Access logging explicitly disabled via JBP_CONFIG_TOMCAT" )
511- return "false"
512- }
513- }
482+ if t .config .AccessLoggingSupport .AccessLogging == "enabled" || t .config .AccessLoggingSupport .AccessLogging == "true" {
483+ t .context .Log .Info ("Access logging enabled via JBP_CONFIG_TOMCAT" )
484+ return "true"
514485 }
515486
516- // Default to disabled (matches Ruby buildpack default)
517487 t .context .Log .Info ("Access logging disabled by default (use JBP_CONFIG_TOMCAT to enable)" )
518488 return "false"
519489}
520490
521491// isExternalConfigurationEnabled checks if external configuration is enabled in config
522492// Returns: (enabled bool, repositoryRoot string, version string)
523493func (t * TomcatContainer ) isExternalConfigurationEnabled () (bool , string , string ) {
524- // Read buildpack configuration from environment or config file
525- // The libbuildpack Stager provides access to buildpack config
526-
527- // Check for JBP_CONFIG_TOMCAT environment variable
528- configEnv := os .Getenv ("JBP_CONFIG_TOMCAT" )
529- if configEnv != "" {
530- // Parse the configuration to check external_configuration_enabled
531- // For now, we'll do a simple string check
532- // A full implementation would parse the YAML/JSON
533- t .context .Log .Debug ("JBP_CONFIG_TOMCAT: %s" , configEnv )
534-
535- // Simple check for external_configuration_enabled: true
536- if strings .Contains (configEnv , "external_configuration_enabled" ) &&
537- (strings .Contains (configEnv , "true" ) || strings .Contains (configEnv , "True" )) {
538-
539- // Extract repository_root and version if present
540- repositoryRoot := extractRepositoryRoot (configEnv )
541- version := extractVersion (configEnv )
542- return true , repositoryRoot , version
543- }
494+ if t .config .Tomcat .ExternalConfigurationEnabled {
495+ repositoryRoot := t .config .ExternalConfiguration .RepositoryRoot
496+ version := t .config .ExternalConfiguration .Version
497+ return true , repositoryRoot , version
544498 }
545499
546500 // Default to false (disabled)
547501 return false , "" , ""
548502}
549503
550- // extractRepositoryRoot extracts the repository_root value from config string
551- func extractRepositoryRoot (config string ) string {
552- // Simple extraction - look for repository_root: "value"
553- // This is a basic implementation; a full parser would use YAML/JSON libraries
554-
555- // Look for repository_root: "..."
556- if idx := strings .Index (config , "repository_root" ); idx != - 1 {
557- remaining := config [idx :]
558- // Find the opening quote
559- if startQuote := strings .Index (remaining , "\" " ); startQuote != - 1 {
560- remaining = remaining [startQuote + 1 :]
561- // Find the closing quote
562- if endQuote := strings .Index (remaining , "\" " ); endQuote != - 1 {
563- return remaining [:endQuote ]
564- }
565- }
566- }
567-
568- return ""
569- }
570-
571- // extractVersion extracts the version value from config string
572- func extractVersion (config string ) string {
573- // Look for version: "value" in the external_configuration section
574- // This is a basic implementation; a full parser would use YAML/JSON libraries
575-
576- // Find external_configuration section first
577- if idx := strings .Index (config , "external_configuration" ); idx != - 1 {
578- remaining := config [idx :]
579- // Look for version: "..."
580- if versionIdx := strings .Index (remaining , "version" ); versionIdx != - 1 {
581- remaining = remaining [versionIdx :]
582- // Find the opening quote
583- if startQuote := strings .Index (remaining , "\" " ); startQuote != - 1 {
584- remaining = remaining [startQuote + 1 :]
585- // Find the closing quote
586- if endQuote := strings .Index (remaining , "\" " ); endQuote != - 1 {
587- return remaining [:endQuote ]
588- }
589- }
590- }
591- }
592-
593- return ""
594- }
595-
596504func injectDocBase (xmlContent string , docBase string ) string {
597505 idx := strings .Index (xmlContent , "<Context" )
598506 if idx == - 1 {
@@ -643,7 +551,7 @@ func (t *TomcatContainer) Finalize() error {
643551 t .context .Log .BeginStep ("Finalizing Tomcat" )
644552
645553 buildDir := t .context .Stager .BuildDir ()
646- contextXMLPath := filepath .Join (t .context . Stager . DepDir (), "tomcat" , "conf" , "Catalina" , "localhost" , "ROOT.xml" )
554+ contextXMLPath := filepath .Join (t .tomcatDir () , "conf" , "Catalina" , "localhost" , "ROOT.xml" )
647555
648556 webInf := filepath .Join (buildDir , "WEB-INF" )
649557 if _ , err := os .Stat (webInf ); err == nil {
@@ -693,3 +601,52 @@ func (t *TomcatContainer) Release() (string, error) {
693601
694602 return cmd , nil
695603}
604+
605+ func (t * TomcatContainer ) tomcatDir () string {
606+ return filepath .Join (t .context .Stager .DepDir (), "tomcat" )
607+ }
608+
609+ func (t * TomcatContainer ) loadConfig () (* tomcatConfig , error ) {
610+ tConfig := tomcatConfig {
611+ Tomcat : Tomcat {
612+ Version : "" ,
613+ ExternalConfigurationEnabled : false ,
614+ },
615+ ExternalConfiguration : ExternalConfiguration {
616+ Version : "" ,
617+ RepositoryRoot : "" ,
618+ },
619+ AccessLoggingSupport : AccessLoggingSupport {
620+ AccessLogging : "disabled" ,
621+ },
622+ }
623+ config := os .Getenv ("JBP_CONFIG_TOMCAT" )
624+ if config != "" {
625+ yamlHandler := common.YamlHandler {}
626+ // overlay JBP_CONFIG_TOMCAT over default values
627+ if err := yamlHandler .Unmarshal ([]byte (config ), & tConfig ); err != nil {
628+ return nil , fmt .Errorf ("failed to parse JBP_CONFIG_TOMCAT: %w" , err )
629+ }
630+ }
631+ return & tConfig , nil
632+ }
633+
634+ type tomcatConfig struct {
635+ Tomcat Tomcat `yaml:"tomcat"`
636+ ExternalConfiguration ExternalConfiguration `yaml:"external_configuration"`
637+ AccessLoggingSupport AccessLoggingSupport `yaml:"access_logging_support"`
638+ }
639+
640+ type Tomcat struct {
641+ Version string `yaml:"version"`
642+ ExternalConfigurationEnabled bool `yaml:"external_configuration_enabled"`
643+ }
644+
645+ type ExternalConfiguration struct {
646+ Version string `yaml:"version"`
647+ RepositoryRoot string `yaml:"repository_root"`
648+ }
649+
650+ type AccessLoggingSupport struct {
651+ AccessLogging string `yaml:"access_logging"`
652+ }
0 commit comments