Skip to content

Commit dd456b4

Browse files
[go-migration] Fix Groovy start command construction with regard to classpath (#1215)
* Refactor Groovy command construction with classpath * Add container security provider * Adjust profile.d for additional classpath * Consider CLASSPATH * Refactoring + comment * Adjust groovy container * Add unit tests * Add integration test
1 parent 03a881f commit dd456b4

6 files changed

Lines changed: 129 additions & 2 deletions

File tree

src/integration/groovy_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@ func testGroovy(platform switchblade.Platform, fixtures string) func(*testing.T,
8585
Expect(logs.String()).To(ContainSubstring("Java Buildpack"))
8686
Eventually(deployment).Should(matchers.Serve(Not(BeEmpty())))
8787
})
88+
89+
it("includes lib/ JARs in the start command classpath", func() {
90+
_, logs, err := platform.Deploy.
91+
WithEnv(map[string]string{
92+
"BP_JAVA_VERSION": "11",
93+
}).
94+
Execute(name, filepath.Join(fixtures, "containers", "groovy_with_lib_jars"))
95+
Expect(err).NotTo(HaveOccurred(), logs.String)
96+
97+
// The finalizer logs "Web process command: <cmd>" during staging.
98+
// Assert the lib JAR appears in the -cp flag of that command.
99+
Expect(logs.String()).To(ContainSubstring("Web process command:"))
100+
Expect(logs.String()).To(ContainSubstring("-cp "))
101+
Expect(logs.String()).To(ContainSubstring("mylib.jar"))
102+
})
88103
})
89104

90105
context("with edge cases", func() {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Cloud Foundry Java Buildpack
3+
* Copyright 2013-2020 the original author or authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
@Grab('io.undertow:undertow-core:2.2.24.Final')
19+
import io.undertow.Undertow
20+
import io.undertow.server.HttpHandler
21+
import io.undertow.server.HttpServerExchange
22+
import io.undertow.util.Headers
23+
24+
def port = System.getenv('PORT') ?: '8080'
25+
26+
Undertow.builder()
27+
.addHttpListener(port.toInteger(), "0.0.0.0")
28+
.setHandler({ HttpServerExchange exchange ->
29+
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain")
30+
exchange.getResponseSender().send("Hello World")
31+
} as HttpHandler)
32+
.build()
33+
.start()
34+
35+
Thread.sleep(Long.MAX_VALUE)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ã(
Binary file not shown.

src/java/containers/groovy.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package containers
22

33
import (
4-
"github.com/cloudfoundry/java-buildpack/src/java/common"
54
"fmt"
5+
"github.com/cloudfoundry/java-buildpack/src/java/common"
66
"os"
77
"path/filepath"
8+
"strings"
89
)
910

1011
// GroovyContainer handles Groovy script applications
@@ -120,8 +121,45 @@ func (g *GroovyContainer) Release() (string, error) {
120121
return "", fmt.Errorf("no Groovy script specified (set GROOVY_SCRIPT)")
121122
}
122123

124+
// Build classpath from all JARs under the app root (mirrors Ruby buildpack's add_libs + as_classpath)
125+
cpFlag := g.buildClasspath()
126+
123127
// Note: JAVA_OPTS is set via environment variables (profile.d/java_opts.sh)
124128
// The groovy command reads JAVA_OPTS from the environment, not command-line args
125-
cmd := fmt.Sprintf("$GROOVY_HOME/bin/groovy %s", mainScript)
129+
var cmd string
130+
if cpFlag != "" {
131+
cmd = fmt.Sprintf("$GROOVY_HOME/bin/groovy %s %s", cpFlag, mainScript)
132+
} else {
133+
cmd = fmt.Sprintf("$GROOVY_HOME/bin/groovy %s", mainScript)
134+
}
126135
return cmd, nil
127136
}
137+
138+
// buildClasspath globs all JARs under the build dir and returns a "-cp <...>" flag string
139+
// with runtime-relative paths (using $HOME), mirroring the Ruby buildpack's add_libs behaviour.
140+
func (g *GroovyContainer) buildClasspath() string {
141+
buildDir := g.context.Stager.BuildDir()
142+
143+
var jarPaths []string
144+
err := filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
145+
if err != nil || info.IsDir() {
146+
return err
147+
}
148+
if strings.HasSuffix(info.Name(), ".jar") {
149+
rel, relErr := filepath.Rel(buildDir, path)
150+
if relErr == nil {
151+
jarPaths = append(jarPaths, "$HOME/"+filepath.ToSlash(rel))
152+
}
153+
}
154+
return nil
155+
})
156+
if err != nil {
157+
g.context.Log.Debug("Error walking build dir for JARs: %s", err.Error())
158+
}
159+
160+
if len(jarPaths) == 0 {
161+
return "-cp ${CLASSPATH:+:$CLASSPATH}${CONTAINER_SECURITY_PROVIDER:+:$CONTAINER_SECURITY_PROVIDER}"
162+
}
163+
// Adding also container security provider and the additional CLASSPATH env built when profile.d scripts are sourced
164+
return "-cp " + strings.Join(jarPaths, ":") + "${CLASSPATH:+:$CLASSPATH}${CONTAINER_SECURITY_PROVIDER:+:$CONTAINER_SECURITY_PROVIDER}"
165+
}

src/java/containers/groovy_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,44 @@ var _ = Describe("Groovy Container", func() {
140140
Expect(err.Error()).To(ContainSubstring("no Groovy script specified"))
141141
})
142142
})
143+
144+
Context("with lib JARs in the build directory", func() {
145+
BeforeEach(func() {
146+
os.WriteFile(filepath.Join(buildDir, "app.groovy"), []byte("println 'app'"), 0644)
147+
os.MkdirAll(filepath.Join(buildDir, "lib"), 0755)
148+
os.WriteFile(filepath.Join(buildDir, "lib", "mylib.jar"), []byte(""), 0644)
149+
os.WriteFile(filepath.Join(buildDir, "lib", "other.jar"), []byte(""), 0644)
150+
container.Detect()
151+
})
152+
153+
It("includes lib JARs in the classpath via -cp flag", func() {
154+
cmd, err := container.Release()
155+
Expect(err).NotTo(HaveOccurred())
156+
Expect(cmd).To(ContainSubstring("-cp "))
157+
Expect(cmd).To(ContainSubstring("$HOME/lib/mylib.jar"))
158+
Expect(cmd).To(ContainSubstring("$HOME/lib/other.jar"))
159+
Expect(cmd).To(ContainSubstring("${CLASSPATH:+:$CLASSPATH}${CONTAINER_SECURITY_PROVIDER:+:$CONTAINER_SECURITY_PROVIDER}"))
160+
})
161+
162+
It("places the -cp flag before the script name", func() {
163+
cmd, err := container.Release()
164+
Expect(err).NotTo(HaveOccurred())
165+
Expect(cmd).To(MatchRegexp(`-cp .+ app\.groovy$`))
166+
})
167+
})
168+
169+
Context("with no JARs anywhere", func() {
170+
BeforeEach(func() {
171+
os.WriteFile(filepath.Join(buildDir, "app.groovy"), []byte("println 'app'"), 0644)
172+
container.Detect()
173+
})
174+
175+
It("omits the -cp flag", func() {
176+
cmd, err := container.Release()
177+
Expect(err).NotTo(HaveOccurred())
178+
Expect(cmd).To(Equal("$GROOVY_HOME/bin/groovy -cp ${CLASSPATH:+:$CLASSPATH}${CONTAINER_SECURITY_PROVIDER:+:$CONTAINER_SECURITY_PROVIDER} app.groovy"))
179+
})
180+
})
143181
})
144182

145183
Describe("Finalize", func() {

0 commit comments

Comments
 (0)