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.concurrent.TimeUnit;
028import java.util.concurrent.atomic.AtomicBoolean;
029import java.util.stream.Collectors;
030
031import static io.micronaut.maven.MojoUtils.findJavaExecutable;
032
033/**
034 * Default implementation for {@link ServerFactory}.
035 *
036 * @author Álvaro Sánchez-Mariscal
037 * @since 4.0.0
038 */
039public class DefaultServerFactory implements ServerFactory {
040
041    private final Log log;
042    private final ToolchainManager toolchainManager;
043    private final MavenSession mavenSession;
044    private final AtomicBoolean serverStarted;
045    private final String testResourcesVersion;
046    private final boolean debugServer;
047    private final boolean foreground;
048
049    private Process process;
050
051    public DefaultServerFactory(Log log,
052                                ToolchainManager toolchainManager,
053                                MavenSession mavenSession,
054                                AtomicBoolean serverStarted,
055                                String testResourcesVersion,
056                                boolean debugServer,
057                                boolean foreground) {
058        this.log = log;
059        this.toolchainManager = toolchainManager;
060        this.mavenSession = mavenSession;
061        this.serverStarted = serverStarted;
062        this.testResourcesVersion = testResourcesVersion;
063        this.debugServer = debugServer;
064        this.foreground = foreground;
065    }
066
067    @Override
068    public void startServer(ServerUtils.ProcessParameters processParameters) {
069        log.info("Starting Micronaut Test Resources service, version " + testResourcesVersion);
070        var cli = new ArrayList<String>();
071
072        String javaBin = findJavaExecutable(toolchainManager, mavenSession);
073        if (javaBin == null) {
074            throw new IllegalStateException("Java executable not found");
075        }
076        cli.add(javaBin);
077        cli.addAll(processParameters.getJvmArguments());
078        if (debugServer) {
079            cli.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000");
080        }
081        processParameters.getSystemProperties().forEach((key, value) -> cli.add("-D" + key + "=" + value));
082        cli.add("-cp");
083        cli.add(processParameters.getClasspath().stream()
084            .map(File::getAbsolutePath)
085            .collect(Collectors.joining(File.pathSeparator)));
086        String mainClass = processParameters.getMainClass();
087        if (mainClass == null) {
088            throw new IllegalStateException("Main class is not set");
089        }
090        cli.add(mainClass);
091        cli.addAll(processParameters.getArguments());
092
093        if (log.isDebugEnabled()) {
094            log.debug(String.format("Command parameters: %s", String.join(" ", cli)));
095        }
096
097        var builder = new ProcessBuilder(cli);
098        try {
099            process = builder.inheritIO().start();
100            if (foreground) {
101                log.info("Test Resources Service started in foreground. Press Ctrl+C to stop.");
102                process.waitFor();
103            }
104        } catch (InterruptedException e) {
105            log.error("Failed to start server", e);
106            Thread.currentThread().interrupt();
107        } catch (Exception e) {
108            log.error("Failed to start server", e);
109            serverStarted.set(false);
110            if (process != null) {
111                process.destroyForcibly();
112            }
113        } finally {
114            if (process != null) {
115                if (process.isAlive()) {
116                    serverStarted.set(true);
117                } else {
118                    process.destroyForcibly();
119                }
120            }
121        }
122    }
123
124    @Override
125    public void waitFor(Duration duration) throws InterruptedException {
126        if (process != null) {
127            process.waitFor(duration.toMillis(), TimeUnit.MILLISECONDS);
128        }
129    }
130}