Skip to content

fix(tomcat): restore context_path support from Ruby buildpack#1333

Open
stokpop wants to merge 9 commits into
cloudfoundry:mainfrom
stokpop:fix/tomcat-context-path
Open

fix(tomcat): restore context_path support from Ruby buildpack#1333
stokpop wants to merge 9 commits into
cloudfoundry:mainfrom
stokpop:fix/tomcat-context-path

Conversation

@stokpop

@stokpop stokpop commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Restores context_path\ support from the Ruby buildpack. Users setting JBP_CONFIG_TOMCAT: {tomcat: {context_path: /foo/bar}} always got ROOT.xml and their app deployed at / — the field was never ported to the Go rewrite.

Changes

  • Adds ContextPath field to Tomcat config struct
  • contextXMLFilename() helper: /foo/barfoo#bar.xml (Tomcat convention)
  • Finalize() reads config and writes the correct context descriptor:
    • Exploded WAR (WEB-INF/ present): docBase="${user.home}/app"
    • Packaged WAR (*.war present): docBase="${user.home}/app/<name>.war"
    • Empty or / context path → ROOT.xml (unchanged default)
    • Trailing slash in context_path normalised
    • Pre-existing non-root ROOT.xml removed to avoid ambiguous deployment
  • External config ordering fix: if a context XML descriptor already exists (e.g. placed by external_configuration_enabled), the generated one is skipped — external config wins
  • RUBY_VS_GO_BUILDPACK_COMPARISON.md updated with context_path parity note and mechanism difference (§2A.2, §2A.11)

Fixes #1331.

Tests

Unit tests (tomcat_test.go, TDD):

  • context_path: /the/intended/paththe#intended#path.xml created, ROOT.xml absent
  • Empty or / context path → ROOT.xml
  • Trailing slash normalised
  • Pre-existing ROOT.xml removed when non-root context path set
  • Explicit empty string via JBP_CONFIG_TOMCATROOT.xml
  • Packaged WAR + context_path → context XML with docBase pointing to WAR file
  • Packaged WAR + no context_pathROOT.xml with WAR docBase
  • Packaged WAR + non-root removes pre-existing ROOT.xml
  • External config descriptor present → write skipped, file unchanged

Integration test (src/integration/tomcat_test.go):

  • Deploys tomcat_jakarta fixture with context_path: /my/app, verifies /my/app serves app content and / does not

context_path was implemented in the Ruby buildpack (commit 0186270,
2015) but never ported to Go. Users setting JBP_CONFIG_TOMCAT
context_path always got ROOT.xml regardless of config.

Tomcat convention: /foo/bar → foo#bar.xml (strip leading /, replace /
with #). Empty or / → ROOT.xml (root context, existing default).

Changes:
- Add ContextPath field to Tomcat config struct (yaml:"context_path")
- Add contextXMLFilename() helper implementing the path→filename transform
- Finalize() loads config if not already loaded (Supply() may not have
  run in all call paths), computes XML filename from context_path
- Three new tests (TDD): context_path set, empty, and /

Fixes cloudfoundry#1331.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Restores support for configuring Tomcat’s deployment context via JBP_CONFIG_TOMCAT.tomcat.context_path, matching the legacy Ruby buildpack behavior (and fixing #1331) by creating the appropriate context XML filename instead of always writing ROOT.xml.

Changes:

  • Add ContextPath to the Tomcat config struct and derive the context XML filename from it.
  • Update Finalize() to write the context XML to the computed filename.
  • Add unit tests covering configured, empty, and / context paths.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/java/containers/tomcat.go Adds context_path config support and uses it during Finalize() to select the context XML filename.
src/java/containers/tomcat_test.go Adds Finalize() tests validating filename selection for configured/empty/root context paths.
Comments suppressed due to low confidence (1)

src/java/containers/tomcat.go:617

  • The write error message still says failed to write ROOT.xml, but the target file may be something else when context_path is configured. This will make failures harder to diagnose.
		if err := os.WriteFile(contextXMLPath, []byte(contextContent), 0644); err != nil {
			return fmt.Errorf("failed to write ROOT.xml: %w", err)
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/java/containers/tomcat.go
Comment thread src/java/containers/tomcat.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment thread src/java/containers/tomcat_test.go
Comment thread src/java/containers/tomcat_test.go
Comment thread src/java/containers/tomcat_test.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread src/java/containers/tomcat.go
Comment thread src/java/containers/tomcat_test.go
…igured; add explicit empty context_path test
@stokpop

stokpop commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Note: context_path deployment mechanism differs between Ruby and Go buildpack

Ruby buildpack (4.x) deploys the app into tomcat/webapps/<name>/ (e.g. webapps/foo#bar/) — Tomcat autodeploys from the webapps directory. No context descriptor XML is involved.

Go buildpack (5.x) uses Tomcat context descriptor files (conf/Catalina/localhost/<name>.xml) with docBase pointing at ${user.home}/app. This means the app is never placed in the webapps/ directory at all — even for the default root context (ROOT.xml points docBase at the app dir).

User-visible URL routing is identical: the application is accessible only at the configured path.

Potential behavioral difference: ServletContext.getRealPath() and any webapps-relative path assumptions will behave differently. In Ruby 4.x, getRealPath("/") resolves under tomcat/webapps/ROOT/ (or webapps/foo#bar/). In Go 5.x, it resolves under ${user.home}/app. This is a structural difference introduced by the Ruby→Go migration, not specific to this context_path PR — but this PR makes it visible for the non-root context case.

Question for reviewers: Is this getRealPath() behavioral difference expected/acceptable, or should the Go buildpack also place (or symlink) the app under tomcat/webapps/<name>/ to maintain full parity?

Details added to RUBY_VS_GO_BUILDPACK_COMPARISON.md §2A.11.

@ramonskie

Copy link
Copy Markdown
Contributor

Finding: context_path fix only covers exploded WARs.

Current Detect() accepts both:

  • exploded WAR: WEB-INF/
  • packaged WAR: *.war

But Finalize() only writes context descriptor when WEB-INF/ exists. So app.war + JBP_CONFIG_TOMCAT='{tomcat:{context_path:/foo}}' still no-op: no foo.xml, no ROOT.xml rewrite/removal.

Also integration test named “WAR file” does not deploy real .war; it deploys exploded tomcat_jakarta fixture (WEB-INF/...). Repo has no .war fixture.

ROOT.xml history matters here: current buildpack writes ROOT.xml after external config, so external config cannot reliably override root context. This PR helps that for exploded apps, but we need coverage proving:

  1. /foo serves OK
  2. / does not serve app
  3. packaged .war behavior fixed or explicitly unsupported
  4. external config + pre-existing ROOT.xml does not double-deploy

Without that, PR fixes happy path only, not full ROOT.xml/context_path problem.

stokpop added 3 commits June 26, 2026 11:51
…r context_path

- Finalize() now writes context XML for packaged .war apps (docBase points
  to the WAR file), so context_path works for both exploded and packaged WARs
- Skip writing context XML if the file already exists (external config
  via external_configuration_enabled provides its own context descriptor)
- Unit tests (TDD): 4 new tests covering WAR path, external-config guard
- Integration test: verify context_path routes /my/app correctly and / returns 404

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

Comment thread src/java/containers/tomcat.go Outdated
Comment thread src/java/containers/tomcat.go
Comment thread src/java/containers/tomcat.go Outdated
Comment thread src/java/containers/tomcat.go
…on-root context_path

- os.Stat unexpected errors (non-IsNotExist) now returned as failures instead
  of silently treating the context XML as already existing
- ROOT.xml removal now happens regardless of whether context XML was generated
  or already existed from external config, so non-root context_path always
  removes a stale ROOT.xml
- Unit test added (TDD) for external-config + non-root ROOT.xml removal
@stokpop

stokpop commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@ramonskie thanks for the info, updates the PR and code based on your feedback, please have a look

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

return fmt.Errorf("failed to remove ROOT.xml: %w", err)
}
}
} else {
}
} else {
warMatches, err := filepath.Glob(filepath.Join(buildDir, "*.war"))
if err == nil && len(warMatches) == 1 {
}
}
} else if len(warMatches) > 1 {
t.context.Log.Warning("Multiple WAR files found in build directory; context_path not applied")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tomcat JBP_CONFIG_TOMCAT context_path not applied in v5

3 participants