@@ -555,68 +555,93 @@ func extractVersion(config string) string {
555555 return ""
556556}
557557
558+ func injectDocBase (xmlContent string , docBase string ) string {
559+ idx := strings .Index (xmlContent , "<Context" )
560+ if idx == - 1 {
561+ return xmlContent
562+ }
563+
564+ endIdx := strings .Index (xmlContent [idx :], ">" )
565+ if endIdx == - 1 {
566+ return xmlContent
567+ }
568+ endIdx += idx
569+
570+ contextTag := xmlContent [idx :endIdx ]
571+
572+ for strings .Contains (contextTag , "docBase=" ) {
573+ docBaseIdx := strings .Index (contextTag , "docBase=" )
574+
575+ if docBaseIdx + 8 >= len (contextTag ) {
576+ break
577+ }
578+ quote := contextTag [docBaseIdx + 8 ]
579+ if quote != '"' && quote != '\'' {
580+ break
581+ }
582+
583+ endQuoteIdx := strings .Index (contextTag [docBaseIdx + 9 :], string (quote ))
584+ if endQuoteIdx == - 1 {
585+ break
586+ }
587+ endQuoteIdx += docBaseIdx + 9
588+
589+ before := strings .TrimSpace (contextTag [:docBaseIdx ])
590+ after := strings .TrimSpace (contextTag [endQuoteIdx + 1 :])
591+ if before != "" && after != "" {
592+ contextTag = before + " " + after
593+ } else {
594+ contextTag = before + after
595+ }
596+ }
597+
598+ newContextTag := strings .Replace (contextTag , "<Context" , `<Context docBase="` + docBase + `"` , 1 )
599+
600+ return xmlContent [:idx ] + newContextTag + xmlContent [endIdx :]
601+ }
602+
558603// Finalize performs final Tomcat configuration
559604func (t * TomcatContainer ) Finalize () error {
560605 t .context .Log .BeginStep ("Finalizing Tomcat" )
561606
562607 buildDir := t .context .Stager .BuildDir ()
563- tomcatDir := filepath .Join (t .context .Stager .DepDir (), "tomcat" )
608+ contextXMLPath := filepath .Join (t .context .Stager .DepDir (), "tomcat" , "conf" , "Catalina" , "localhost" , "ROOT.xml " )
564609
565- // Check if we have an exploded WAR (WEB-INF directory in BuildDir)
566610 webInf := filepath .Join (buildDir , "WEB-INF" )
567611 if _ , err := os .Stat (webInf ); err == nil {
568- // Configure Tomcat to serve the application from BuildDir
569- // This follows the immutable BuildDir pattern: application stays where deployed
570- t .context .Log .Info ("Configuring Tomcat to serve exploded WAR from BuildDir" )
571-
572- // Create a custom context.xml file that points to BuildDir
573- // At runtime, $HOME will resolve to the application directory
574- if err := t .configureContextDocBase (tomcatDir ); err != nil {
575- return fmt .Errorf ("failed to configure Tomcat context: %w" , err )
612+ contextXMLDir := filepath .Dir (contextXMLPath )
613+ if err := os .MkdirAll (contextXMLDir , 0755 ); err != nil {
614+ return fmt .Errorf ("failed to create context directory: %w" , err )
576615 }
577616
578- t .context .Log .Info ("Tomcat configured to serve application from $HOME (BuildDir)" )
579- }
580-
581- // Tomcat support JARs are already installed directly to tomcat/lib during Supply phase
582- // No additional configuration needed in Finalize phase
583-
584- // JVMKill agent is configured by JRE component in JAVA_OPTS
617+ appContextXML := filepath .Join (buildDir , "META-INF" , "context.xml" )
618+ var contextContent string
585619
586- return nil
587- }
620+ if _ , err := os .Stat (appContextXML ); err == nil {
621+ xmlBytes , err := os .ReadFile (appContextXML )
622+ if err != nil {
623+ return fmt .Errorf ("failed to read META-INF/context.xml: %w" , err )
624+ }
588625
589- // configureContextDocBase creates a context configuration that points to BuildDir
590- func (t * TomcatContainer ) configureContextDocBase (tomcatHome string ) error {
591- // Create conf/Catalina/localhost directory if it doesn't exist
592- contextDir := filepath .Join (tomcatHome , "conf" , "Catalina" , "localhost" )
593- if err := os .MkdirAll (contextDir , 0755 ); err != nil {
594- return fmt .Errorf ("failed to create context directory: %w" , err )
595- }
626+ xmlStr := string (xmlBytes )
627+ xmlStr = strings .TrimSpace (xmlStr )
596628
597- // Create ROOT.xml context file
598- // This tells Tomcat to serve the ROOT webapp from BuildDir (the application directory)
599- // Tomcat supports ${propertyName} syntax for system properties in context.xml
600- contextFile := filepath .Join (contextDir , "ROOT.xml" )
601- contextXML := `<?xml version="1.0" encoding="UTF-8"?>
602- <Context docBase="${user.home}/app" reloadable="false">
603- <!-- Application served from BuildDir (/home/vcap/app), not moved to DepDir -->
604- <!-- At runtime: user.home system property = /home/vcap, so we use ${user.home}/app -->
605- </Context>
606- `
629+ contextContent = injectDocBase (xmlStr , "${user.home}/app" )
630+ t .context .Log .Info ("Merged META-INF/context.xml with ROOT.xml - realm and resource configurations preserved" )
631+ } else {
632+ contextContent = fmt .Sprintf ("<Context docBase=\" ${user.home}/app\" reloadable=\" false\" >\n </Context>\n " )
633+ t .context .Log .Info ("Created ROOT.xml with docBase pointing to application directory" )
634+ }
607635
608- if err := os .WriteFile (contextFile , []byte (contextXML ), 0644 ); err != nil {
609- return fmt .Errorf ("failed to write context file: %w" , err )
636+ if err := os .WriteFile (contextXMLPath , []byte (contextContent ), 0644 ); err != nil {
637+ return fmt .Errorf ("failed to write ROOT.xml: %w" , err )
638+ }
610639 }
611640
612- t .context .Log .Debug ("Created Tomcat context configuration: %s" , contextFile )
613641 return nil
614642}
615643
616- // configureTomcatSupport copies Tomcat lifecycle support JAR to Tomcat's lib directory
617- // This ensures the JAR is loaded early enough for logging initialization
618644// Release returns the Tomcat startup command
619- // Uses $CATALINA_HOME which is set by profile.d/tomcat.sh at runtime
620645func (t * TomcatContainer ) Release () (string , error ) {
621646 // Use $CATALINA_HOME environment variable set by profile.d script
622647 // Profile.d scripts run BEFORE the release command at runtime (same as $JAVA_HOME)
0 commit comments