-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ Camel Spring Boot on Tomcat App/Product #981
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package software.tnb.product.csb; | ||
|
||
import org.apache.maven.model.Dependency; | ||
|
||
import software.tnb.product.LocalProduct; | ||
import software.tnb.product.Product; | ||
import software.tnb.product.application.App; | ||
import software.tnb.product.csb.application.TomcatSpringBootApp; | ||
import software.tnb.product.customizer.Customizer; | ||
import software.tnb.product.customizer.component.rest.RestCustomizer; | ||
import software.tnb.product.integration.builder.AbstractIntegrationBuilder; | ||
|
||
import com.google.auto.service.AutoService; | ||
|
||
import software.tnb.product.util.maven.Maven; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck> reported by reviewdog 🐶 |
||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@AutoService(Product.class) | ||
public class TomcatCamelSpringBoot extends LocalProduct { | ||
|
||
private TomcatSpringBootApp app; | ||
|
||
@Override | ||
public App createIntegrationApp(AbstractIntegrationBuilder<?> integrationBuilder) { | ||
// Let's remove restcustomizer, since it exclude tomcat | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say restcustomizer (and all customizers) should be changed to handle the tomcat as well, rather than changing it here |
||
List<Customizer> customizers = integrationBuilder.getCustomizers() | ||
.stream().filter(customizer -> customizer instanceof RestCustomizer) | ||
.collect(Collectors.toList()); | ||
integrationBuilder.getCustomizers().remove(customizers); | ||
|
||
// spring web is needed and tomcat is provided | ||
Dependency providedTomcatDependency = new Dependency(); | ||
providedTomcatDependency.setArtifactId("spring-boot-starter-tomcat"); | ||
providedTomcatDependency.setGroupId("org.springframework.boot"); | ||
providedTomcatDependency.setScope("provided"); | ||
|
||
integrationBuilder.dependencies( | ||
Maven.createDependency("org.springframework.boot:spring-boot-starter-web"), | ||
providedTomcatDependency); | ||
|
||
app = new TomcatSpringBootApp(integrationBuilder); | ||
return app; | ||
} | ||
|
||
@Override | ||
public void setupProduct() { | ||
super.setupProduct(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not necessary here since it only calls super |
||
|
||
@Override | ||
public void teardownProduct() { | ||
app.stop(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. apps are stopped via |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
package software.tnb.product.csb.application; | ||
|
||
import software.tnb.common.config.TestConfiguration; | ||
import software.tnb.product.application.App; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck> reported by reviewdog 🐶 |
||
import software.tnb.product.application.Phase; | ||
import software.tnb.product.csb.configuration.SpringBootConfiguration; | ||
import software.tnb.product.customizer.Customizer; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck> reported by reviewdog 🐶 |
||
import software.tnb.product.customizer.component.rest.RestCustomizer; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck> reported by reviewdog 🐶 |
||
import software.tnb.product.integration.builder.AbstractIntegrationBuilder; | ||
import software.tnb.product.log.FileLog; | ||
import software.tnb.product.log.Log; | ||
import software.tnb.product.log.stream.LogStream; | ||
import software.tnb.product.util.ZipUtils; | ||
import software.tnb.product.util.maven.BuildRequest; | ||
import software.tnb.product.util.maven.Maven; | ||
|
||
import org.apache.commons.io.FileUtils; | ||
import org.apache.maven.model.Dependency; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck> reported by reviewdog 🐶 |
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.net.URL; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.List; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck> reported by reviewdog 🐶 |
||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck> reported by reviewdog 🐶 |
||
|
||
public class TomcatSpringBootApp extends SpringBootApp { | ||
private static final Logger LOG = LoggerFactory.getLogger(TomcatSpringBootApp.class); | ||
|
||
private AbstractIntegrationBuilder<?> integrationBuilder; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not used for anything |
||
private Path tomcatTmpDirectory; | ||
private Path tomcatHome = null; | ||
private Process tomcatProcess = null; | ||
private static final String TOMCAT_PARENT_DIRECTORY = "tomcat"; | ||
private static final String TOMCAT_ARCHIVE_NAME = "tomcat-archive.zip"; | ||
|
||
@Override | ||
public Log getLog() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set the |
||
return new FileLog(getLogPath()); | ||
} | ||
|
||
public TomcatSpringBootApp(AbstractIntegrationBuilder<?> integrationBuilder) { | ||
super(integrationBuilder); | ||
|
||
downloadTomcat(); | ||
|
||
this.integrationBuilder = integrationBuilder; | ||
} | ||
|
||
private void startTomcat() { | ||
File[] file = tomcatTmpDirectory.resolve(TOMCAT_PARENT_DIRECTORY).toFile().listFiles(); | ||
for (File f : file) { | ||
if (f.isDirectory()) { | ||
if (f.getName().contains("apache-tomcat")) { | ||
tomcatHome = f.toPath(); | ||
} else if (f.getName().contains("jws")) { | ||
tomcatHome = f.toPath().resolve("tomcat"); // Case JWS | ||
} | ||
} | ||
} | ||
|
||
if (tomcatHome == null) { | ||
throw new RuntimeException("Could not find Tomcat home in " + tomcatTmpDirectory.resolve(TOMCAT_PARENT_DIRECTORY)); | ||
} | ||
|
||
Path logFile = getLogPath(); | ||
// startup.sh starts on another process, let's use catalina run so that we can control the lifecyle | ||
ProcessBuilder processBuilder = new ProcessBuilder(tomcatHome + File.separator + "bin" + File.separator + "catalina.sh", "run") | ||
.redirectError(logFile.toFile()) | ||
.redirectOutput(logFile.toFile()); | ||
|
||
try { | ||
tomcatProcess = processBuilder.start(); | ||
Runtime.getRuntime().addShutdownHook(new Thread(() -> { | ||
if (tomcatProcess != null) { | ||
tomcatProcess.destroyForcibly(); | ||
} | ||
})); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
|
||
LOG.info("Starting tomcat in {}", tomcatHome); | ||
} | ||
|
||
private void downloadTomcat() { | ||
try { | ||
tomcatTmpDirectory = Files.createTempDirectory("tnb-tomcat"); | ||
LOG.info("Downloading tomcat in {}", tomcatTmpDirectory); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. info -> debug |
||
FileUtils.copyURLToFile(new URL(SpringBootConfiguration.tomcatZipUrl()), tomcatTmpDirectory.resolve(TOMCAT_ARCHIVE_NAME).toFile()); | ||
|
||
ZipUtils.unzip(tomcatTmpDirectory.resolve(TOMCAT_ARCHIVE_NAME), tomcatTmpDirectory.resolve(TOMCAT_PARENT_DIRECTORY)); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
|
||
@Override | ||
public void start() { | ||
startTomcat(); | ||
|
||
// replace packaging and extend SpringBootServletInitializer | ||
try { | ||
Path pomPath = TestConfiguration.appLocation().resolve(name).resolve("pom.xml"); | ||
String pom = Files.readString(pomPath); | ||
pom = pom.replace("<artifactId>" + name + "</artifactId>", | ||
"<artifactId>" + name + "</artifactId><packaging>war</packaging>"); | ||
Files.write(pomPath, pom.getBytes(StandardCharsets.UTF_8)); | ||
|
||
Path mainPath = TestConfiguration.appLocation().resolve(name) | ||
.resolve("src") | ||
.resolve("main") | ||
.resolve("java") | ||
.resolve("com") | ||
.resolve("test") | ||
.resolve("MySpringBootApplication.java"); | ||
String main = Files.readString(mainPath); | ||
|
||
main = main.replace("class MySpringBootApplication {", | ||
""" | ||
class MySpringBootApplication extends org.springframework.boot.web.servlet.support.SpringBootServletInitializer { | ||
|
||
@Override | ||
protected org.springframework.boot.builder.SpringApplicationBuilder | ||
configure(org.springframework.boot.builder.SpringApplicationBuilder application) { | ||
return application.sources(MySpringBootApplication.class); | ||
} | ||
"""); | ||
Comment on lines
+109
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if this is something that should be done for all tomcat apps, it should be done by a |
||
Files.write(mainPath, main.getBytes(StandardCharsets.UTF_8)); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
// package application | ||
BuildRequest.Builder requestBuilder = new BuildRequest.Builder() | ||
.withBaseDirectory(TestConfiguration.appLocation().resolve(name)) | ||
.withGoals("clean", "package") | ||
.withProperties(Map.of( | ||
"skipTests", "true" | ||
)) | ||
.withLogFile(getLogPath(Phase.BUILD)) | ||
.withLogMarker(LogStream.marker(name, Phase.BUILD)); | ||
|
||
LOG.info("Building {} application project for tomcat", name); | ||
Maven.invoke(requestBuilder.build()); | ||
|
||
// copy generated WAR in tomcat | ||
String warName = name + "-1.0.0-SNAPSHOT.war"; | ||
try { | ||
Files.copy(TestConfiguration.appLocation().resolve(name).resolve("target").resolve(warName), | ||
tomcatHome.resolve("webapps").resolve(warName)); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
|
||
@Override | ||
public void stop() { | ||
if (logStream != null) { | ||
logStream.stop(); | ||
} | ||
|
||
if (log != null) { | ||
log.save(); | ||
} | ||
|
||
if (tomcatProcess != null) { | ||
try { | ||
Process shutdown = new ProcessBuilder(tomcatHome + File.separator + "bin" + File.separator + "shutdown.sh") | ||
.start(); | ||
shutdown.waitFor(); | ||
|
||
tomcatProcess.destroyForcibly(); | ||
} catch (IOException | InterruptedException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public boolean isReady() { | ||
return tomcatProcess != null && tomcatProcess.isAlive(); | ||
} | ||
|
||
@Override | ||
public boolean isFailed() { | ||
return tomcatProcess != null && !tomcatProcess.isAlive(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,9 @@ public class SpringBootConfiguration extends CamelConfiguration { | |
|
||
public static final String OPENSHIFT_SB_RESULT_IMAGE_REPOSITORY = "openshift-sb.result-image.repo"; | ||
|
||
public static final String TOMCAT_ZIP_DOWNLOAD_URL = "tomcat.zip.download.url"; | ||
public static final String USE_TOMCAT = "test.use.tomcat"; | ||
|
||
public static String springBootVersion() { | ||
return getProperty(SPRINGBOOT_VERSION, "2.6.1"); | ||
} | ||
|
@@ -104,4 +107,12 @@ public static String openshiftBaseImage() { | |
public static String openshiftResultImageRepository() { | ||
return getProperty(OPENSHIFT_SB_RESULT_IMAGE_REPOSITORY); | ||
} | ||
|
||
public static String tomcatZipUrl() { | ||
return getProperty(TOMCAT_ZIP_DOWNLOAD_URL, "https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.23/bin/apache-tomcat-10.1.23.zip"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't the version be configurable? |
||
} | ||
|
||
public static boolean isTomcat() { | ||
return getBoolean(USE_TOMCAT, false); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package software.tnb.product.util; | ||
|
||
import java.io.File; | ||
import java.io.FileInputStream; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.StandardCopyOption; | ||
import java.nio.file.attribute.PosixFilePermission; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.zip.ZipEntry; | ||
import java.util.zip.ZipInputStream; | ||
|
||
public class ZipUtils { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck> reported by reviewdog 🐶 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.design.FinalClassCheck> reported by reviewdog 🐶 |
||
|
||
private ZipUtils() { | ||
} | ||
|
||
public static void unzip(Path source, Path target) throws IOException { | ||
Set<PosixFilePermission> executePermissions = new HashSet<>(); | ||
executePermissions.add(PosixFilePermission.OTHERS_EXECUTE); | ||
executePermissions.add(PosixFilePermission.OTHERS_WRITE); | ||
executePermissions.add(PosixFilePermission.OTHERS_READ); | ||
executePermissions.add(PosixFilePermission.GROUP_EXECUTE); | ||
executePermissions.add(PosixFilePermission.GROUP_WRITE); | ||
executePermissions.add(PosixFilePermission.GROUP_READ); | ||
executePermissions.add(PosixFilePermission.OWNER_EXECUTE); | ||
executePermissions.add(PosixFilePermission.OWNER_WRITE); | ||
executePermissions.add(PosixFilePermission.OWNER_READ); | ||
|
||
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source.toFile()))) { | ||
|
||
// list files in zip | ||
ZipEntry zipEntry = zis.getNextEntry(); | ||
|
||
while (zipEntry != null) { | ||
|
||
boolean isDirectory = zipEntry.getName().endsWith(File.separator); | ||
|
||
Path newPath = zipSlipProtect(zipEntry, target); | ||
|
||
if (isDirectory) { | ||
Files.createDirectories(newPath); | ||
} else { | ||
if (newPath.getParent() != null) { | ||
if (Files.notExists(newPath.getParent())) { | ||
Files.createDirectories(newPath.getParent()); | ||
} | ||
} | ||
|
||
Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING); | ||
if (newPath.getFileName().toString().endsWith(".sh")) { | ||
Files.setPosixFilePermissions(newPath, executePermissions); | ||
} | ||
} | ||
|
||
zipEntry = zis.getNextEntry(); | ||
} | ||
zis.closeEntry(); | ||
|
||
} | ||
|
||
} | ||
|
||
private static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) | ||
throws IOException { | ||
Path targetDirResolved = targetDir.resolve(zipEntry.getName()); | ||
|
||
Path normalizePath = targetDirResolved.normalize(); | ||
if (!normalizePath.startsWith(targetDir)) { | ||
throw new IOException("Bad zip entry: " + zipEntry.getName()); | ||
} | ||
|
||
return normalizePath; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚫 [reviewdog] <com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck> reported by reviewdog 🐶
Wrong order for 'software.tnb.product.LocalProduct' import.