001/* 002 * Copyright 2017-2023 original authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.micronaut.maven.testresources; 017 018import io.micronaut.testresources.buildtools.ServerFactory; 019import io.micronaut.testresources.buildtools.ServerUtils; 020import org.apache.maven.execution.MavenSession; 021import org.apache.maven.plugin.logging.Log; 022import org.apache.maven.toolchain.ToolchainManager; 023 024import java.io.File; 025import java.time.Duration; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.Map; 029import java.util.concurrent.TimeUnit; 030import java.util.concurrent.atomic.AtomicBoolean; 031import java.util.stream.Collectors; 032 033import static io.micronaut.maven.MojoUtils.findJavaExecutable; 034 035/** 036 * Default implementation for {@link ServerFactory}. 037 * 038 * @author Álvaro Sánchez-Mariscal 039 * @since 4.0.0 040 */ 041public class DefaultServerFactory implements ServerFactory { 042 043 private final Log log; 044 private final ToolchainManager toolchainManager; 045 private final MavenSession mavenSession; 046 private final AtomicBoolean serverStarted; 047 private final String testResourcesVersion; 048 private final boolean debugServer; 049 private final boolean foreground; 050 private final Map<String, String> testResourcesSystemProperties; 051 052 private Process process; 053 054 public DefaultServerFactory(Log log, 055 ToolchainManager toolchainManager, 056 MavenSession mavenSession, 057 AtomicBoolean serverStarted, 058 String testResourcesVersion, 059 boolean debugServer, 060 boolean foreground, final Map<String, String> testResourcesSystemProperties) { 061 this.log = log; 062 this.toolchainManager = toolchainManager; 063 this.mavenSession = mavenSession; 064 this.serverStarted = serverStarted; 065 this.testResourcesVersion = testResourcesVersion; 066 this.debugServer = debugServer; 067 this.foreground = foreground; 068 this.testResourcesSystemProperties = testResourcesSystemProperties; 069 } 070 071 @Override 072 public void startServer(ServerUtils.ProcessParameters processParameters) { 073 log.info("Starting Micronaut Test Resources service, version " + testResourcesVersion); 074 var cli = computeCliArguments(processParameters); 075 076 if (log.isDebugEnabled()) { 077 log.debug(String.format("Command parameters: %s", String.join(" ", cli))); 078 } 079 080 var builder = new ProcessBuilder(cli); 081 try { 082 process = builder.inheritIO().start(); 083 if (foreground) { 084 log.info("Test Resources Service started in foreground. Press Ctrl+C to stop."); 085 process.waitFor(); 086 } 087 } catch (InterruptedException e) { 088 log.error("Failed to start server", e); 089 Thread.currentThread().interrupt(); 090 } catch (Exception e) { 091 log.error("Failed to start server", e); 092 serverStarted.set(false); 093 if (process != null) { 094 process.destroyForcibly(); 095 } 096 } finally { 097 if (process != null) { 098 if (process.isAlive()) { 099 serverStarted.set(true); 100 } else { 101 process.destroyForcibly(); 102 } 103 } 104 } 105 } 106 107 /** 108 * Computes the command-line arguments required to run the server based on the provided process parameters. 109 * 110 * @param processParameters the process parameters containing information about JVM arguments, system properties, 111 * classpath, main class, and program arguments 112 * @return a list of command-line arguments as strings 113 * @throws IllegalStateException if the Java executable cannot be found, or if the main class is not set 114 */ 115 List<String> computeCliArguments(ServerUtils.ProcessParameters processParameters) { 116 var cli = new ArrayList<String>(); 117 118 String javaBin = findJavaExecutable(toolchainManager, mavenSession); 119 if (javaBin == null) { 120 throw new IllegalStateException("Java executable not found"); 121 } 122 cli.add(javaBin); 123 cli.addAll(processParameters.getJvmArguments()); 124 if (debugServer) { 125 cli.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000"); 126 } 127 processParameters.getSystemProperties().forEach((key, value) -> cli.add("-D" + key + "=" + value)); 128 if (testResourcesSystemProperties != null && !testResourcesSystemProperties.isEmpty()) { 129 testResourcesSystemProperties.forEach((key, value) -> cli.add("-D" + key + "=" + value)); 130 } 131 cli.add("-cp"); 132 cli.add(processParameters.getClasspath().stream() 133 .map(File::getAbsolutePath) 134 .collect(Collectors.joining(File.pathSeparator))); 135 String mainClass = processParameters.getMainClass(); 136 if (mainClass == null) { 137 throw new IllegalStateException("Main class is not set"); 138 } 139 cli.add(mainClass); 140 cli.addAll(processParameters.getArguments()); 141 142 return cli; 143 } 144 145 @Override 146 public void waitFor(Duration duration) throws InterruptedException { 147 if (process != null) { 148 process.waitFor(duration.toMillis(), TimeUnit.MILLISECONDS); 149 } 150 } 151}