Maven Daemon CLIng (#1158)

Co-authored-by: Guillaume Nodet <gnodet@gmail.com>
This commit is contained in:
Tamas Cservenak
2024-10-15 13:05:56 +02:00
committed by GitHub
parent 06eb5fd2a9
commit 27258c0e54
22 changed files with 626 additions and 1702 deletions

View File

@@ -60,7 +60,11 @@
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<artifactId>maven-resolver-provider</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-cli</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>

View File

@@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.maven.cling.invoker.mvn.CommonsCliMavenOptions;
import org.codehaus.plexus.interpolation.BasicInterpolator;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.mvndaemon.mvnd.common.Environment;
import static org.apache.maven.cling.invoker.Utils.createInterpolator;
public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions implements DaemonMavenOptions {
public static CommonsCliDaemonMavenOptions parse(String source, String[] args) throws ParseException {
CLIManager cliManager = new CLIManager();
return new CommonsCliDaemonMavenOptions(source, cliManager, cliManager.parse(args));
}
protected CommonsCliDaemonMavenOptions(String source, CLIManager cliManager, CommandLine commandLine) {
super(source, cliManager, commandLine);
}
public org.apache.commons.cli.Options getOptions() {
return this.cliManager.getOptions();
}
@Override
public Optional<Boolean> rawStreams() {
if (commandLine.hasOption(CLIManager.RAW_STREAMS)
|| Environment.MVND_RAW_STREAMS.asOptional().isPresent()) {
return Optional.of(Boolean.TRUE);
}
return Optional.empty();
}
private static CommonsCliDaemonMavenOptions interpolate(
CommonsCliDaemonMavenOptions options, Collection<Map<String, String>> properties) {
try {
// now that we have properties, interpolate all arguments
BasicInterpolator interpolator = createInterpolator(properties);
CommandLine.Builder commandLineBuilder = new CommandLine.Builder();
commandLineBuilder.setDeprecatedHandler(o -> {});
for (Option option : options.commandLine.getOptions()) {
if (!CLIManager.USER_PROPERTY.equals(option.getOpt())) {
List<String> values = option.getValuesList();
for (ListIterator<String> it = values.listIterator(); it.hasNext(); ) {
it.set(interpolator.interpolate(it.next()));
}
}
commandLineBuilder.addOption(option);
}
for (String arg : options.commandLine.getArgList()) {
commandLineBuilder.addArg(interpolator.interpolate(arg));
}
return new CommonsCliDaemonMavenOptions(
options.source, (CLIManager) options.cliManager, commandLineBuilder.build());
} catch (InterpolationException e) {
throw new IllegalArgumentException("Could not interpolate CommonsCliOptions", e);
}
}
@Override
public DaemonMavenOptions interpolate(Collection<Map<String, String>> properties) {
return interpolate(this, properties);
}
protected static class CLIManager extends CommonsCliMavenOptions.CLIManager {
public static final String RAW_STREAMS = "ras";
@Override
protected void prepareOptions(Options options) {
super.prepareOptions(options);
options.addOption(Option.builder(RAW_STREAMS)
.longOpt("raw-streams")
.desc("Use raw-streams for daemon communication")
.build());
}
}
}

View File

@@ -26,7 +26,7 @@ import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
/**
* Simple interface to bridge maven 3.9.x and 4.0.x CLI
*/
public interface DaemonCli {
public interface DaemonCli extends AutoCloseable {
int main(
List<String> args,
String workingDir,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.jline.MessageUtils;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.jline.terminal.Terminal;
import org.jline.terminal.impl.ExternalTerminal;
import org.mvndaemon.mvnd.cli.EnvHelper;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
public class DaemonMavenCling implements DaemonCli {
private final DaemonMavenParser parser;
private final DaemonMavenInvoker invoker;
public DaemonMavenCling() {
this.parser = new DaemonMavenParser();
this.invoker = new DaemonMavenInvoker(ProtoLookup.builder()
.addMapping(
ClassWorld.class, ((ClassRealm) Thread.currentThread().getContextClassLoader()).getWorld())
.build());
}
@Override
public void close() throws Exception {
invoker.close();
}
@Override
public int main(
List<String> args,
String workingDir,
String projectDir,
Map<String, String> env,
BuildEventListener buildEventListener)
throws Exception {
Terminal terminal = new ExternalTerminal(
"Maven",
"ansi",
new ByteArrayInputStream(new byte[0]),
new ByteArrayOutputStream(),
StandardCharsets.UTF_8);
MessageUtils.systemInstall(terminal);
EnvHelper.environment(workingDir, env);
System.setProperty("maven.multiModuleProjectDirectory", projectDir);
return invoker.invoke(parser.parse(ParserRequest.builder(
"mvnd", "Maven Daemon", args, new ProtoLogger(), new DaemonMessageBuilderFactory())
.cwd(Paths.get(workingDir))
.lookup(ProtoLookup.builder()
.addMapping(Environment.class, () -> env)
.addMapping(BuildEventListener.class, buildEventListener)
.build())
.build()));
}
/**
* Key for environment.
*/
interface Environment extends Supplier<Map<String, String>> {}
}

View File

@@ -0,0 +1,141 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cli.event.ExecutionEventLogger;
import org.apache.maven.cling.invoker.ContainerCapsuleFactory;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.resident.DefaultResidentMavenInvoker;
import org.apache.maven.execution.ExecutionListener;
import org.apache.maven.jline.MessageUtils;
import org.eclipse.aether.transfer.TransferListener;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.logging.slf4j.MvndSimpleLogger;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener;
import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream;
import org.mvndaemon.mvnd.transfer.DaemonMavenTransferListener;
import org.slf4j.spi.LocationAwareLogger;
public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
public DaemonMavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
}
@Override
protected void configureLogging(LocalContext context) throws Exception {
super.configureLogging(context);
DaemonMavenOptions options = (DaemonMavenOptions) context.invokerRequest.options();
if (options.logFile().isEmpty() && !options.rawStreams().orElse(false)) {
MvndSimpleLogger stdout = (MvndSimpleLogger) context.loggerFactory.getLogger("stdout");
MvndSimpleLogger stderr = (MvndSimpleLogger) context.loggerFactory.getLogger("stderr");
stdout.setLogLevel(LocationAwareLogger.INFO_INT);
stderr.setLogLevel(LocationAwareLogger.INFO_INT);
System.setOut(new LoggingOutputStream(s -> stdout.info("[stdout] " + s)).printStream());
System.setErr(new LoggingOutputStream(s -> stderr.warn("[stderr] " + s)).printStream());
}
}
@Override
protected void helpOrVersionAndMayExit(LocalContext context) throws Exception {
MavenInvokerRequest<MavenOptions> invokerRequest = context.invokerRequest;
BuildEventListener buildEventListener =
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class);
if (invokerRequest.options().help().isPresent()) {
// TODO: ugly, clenup
buildEventListener.log(
MvndHelpFormatter.displayHelp((CommonsCliDaemonMavenOptions) context.invokerRequest.options()));
throw new ExitException(0);
}
if (invokerRequest.options().showVersionAndExit().isPresent()) {
if (invokerRequest.options().quiet().orElse(false)) {
buildEventListener.log(CLIReportingUtils.showVersionMinimal());
} else {
buildEventListener.log(CLIReportingUtils.showVersion());
}
throw new ExitException(0);
}
}
@Override
protected void preCommands(LocalContext context) throws Exception {
Options mavenOptions = context.invokerRequest.options();
if (mavenOptions.verbose().orElse(false) || mavenOptions.showVersion().orElse(false)) {
context.invokerRequest
.parserRequest()
.lookup()
.lookup(BuildEventListener.class)
.log(CLIReportingUtils.showVersion());
}
}
@Override
protected ContainerCapsuleFactory<MavenOptions, MavenInvokerRequest<MavenOptions>, LocalContext>
createContainerCapsuleFactory() {
return new DaemonPlexusContainerCapsuleFactory();
}
@Override
protected ExecutionListener determineExecutionListener(LocalContext context) {
if (context.lookup != null) {
LoggingExecutionListener listener = context.lookup.lookup(LoggingExecutionListener.class);
ExecutionEventLogger executionEventLogger =
new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
listener.init(
context.eventSpyDispatcher.chainListener(executionEventLogger),
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class));
return listener;
} else {
// this branch happens in "early" step of container capsule to load extensions
return super.determineExecutionListener(context);
}
}
@Override
protected TransferListener determineTransferListener(LocalContext context, boolean noTransferProgress) {
return new DaemonMavenTransferListener(
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class),
super.determineTransferListener(context, noTransferProgress));
}
@Override
protected int doExecute(LocalContext context) throws Exception {
context.logger.info(MessageUtils.builder()
.a("Processing build on daemon ")
.strong(Environment.MVND_ID.asString())
.toString());
context.logger.info("Daemon status dump:");
context.logger.info("CWD: " + context.invokerRequest.cwd());
context.logger.info("MAVEN_HOME: " + context.invokerRequest.installationDirectory());
context.logger.info("USER_HOME: " + context.invokerRequest.userHomeDirectory());
context.logger.info("topDirectory: " + context.invokerRequest.topDirectory());
context.logger.info("rootDirectory: " + context.invokerRequest.rootDirectory());
try {
return super.doExecute(context);
} finally {
LoggingOutputStream.forceFlush(System.out);
LoggingOutputStream.forceFlush(System.err);
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import org.apache.maven.api.cli.mvn.MavenOptions;
public interface DaemonMavenOptions extends MavenOptions {
/**
* Should use raw-streams to communicate with daemon.
*/
Optional<Boolean> rawStreams();
@Override
DaemonMavenOptions interpolate(Collection<Map<String, String>> properties);
}

View File

@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.apache.commons.cli.ParseException;
import org.apache.maven.api.cli.ParserException;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.mvn.BaseMavenParser;
import org.apache.maven.cling.invoker.mvn.DefaultMavenInvokerRequest;
import org.mvndaemon.mvnd.common.Environment;
public class DaemonMavenParser extends BaseMavenParser<MavenOptions, MavenInvokerRequest<MavenOptions>> {
@Override
protected DefaultMavenInvokerRequest<MavenOptions> getInvokerRequest(LocalContext context) {
return new DefaultMavenInvokerRequest<>(
context.parserRequest,
context.cwd,
context.installationDirectory,
context.userHomeDirectory,
context.userProperties,
context.systemProperties,
context.topDirectory,
context.rootDirectory,
context.parserRequest.in(),
context.parserRequest.out(),
context.parserRequest.err(),
context.extensions,
(DaemonMavenOptions) context.options);
}
@Override
protected MavenOptions parseArgs(String source, List<String> args) throws ParserException {
try {
return CommonsCliDaemonMavenOptions.parse(source, args.toArray(new String[0]));
} catch (ParseException e) {
throw new ParserException("Failed to parse source " + source + ": " + e.getMessage(), e.getCause());
}
}
@Override
protected MavenOptions assembleOptions(List<MavenOptions> parsedOptions) {
return LayeredDaemonMavenOptions.layerDaemonMavenOptions(
parsedOptions.stream().map(o -> (DaemonMavenOptions) o).toList());
}
@Override
protected Map<String, String> populateSystemProperties(LocalContext context) throws ParserException {
HashMap<String, String> systemProperties = new HashMap<>(super.populateSystemProperties(context));
Map<String, String> env = context.parserRequest
.lookup()
.lookup(DaemonMavenCling.Environment.class)
.get();
systemProperties.putAll(env);
return systemProperties;
}
@Override
protected List<CoreExtension> readCoreExtensionsDescriptor(LocalContext context) {
return Stream.of(Environment.MVND_CORE_EXTENSIONS.asString().split(";"))
.filter(s -> s != null && !s.isEmpty())
.map(s -> {
String[] parts = s.split(":");
return CoreExtension.newBuilder()
.groupId(parts[0])
.artifactId(parts[1])
.version(parts[2])
.build();
})
.toList();
}
}

View File

@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.PlexusContainerCapsuleFactory;
import org.apache.maven.cling.invoker.mvn.resident.DefaultResidentMavenInvoker;
import org.codehaus.plexus.logging.LoggerManager;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager;
public class DaemonPlexusContainerCapsuleFactory
extends PlexusContainerCapsuleFactory<
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultResidentMavenInvoker.LocalContext> {
private final Slf4jLoggerManager slf4jLoggerManager = new Slf4jLoggerManager();
@Override
protected LoggerManager createLoggerManager() {
return slf4jLoggerManager;
}
@Override
protected List<Path> parseExtClasspath(DefaultResidentMavenInvoker.LocalContext context) throws Exception {
return Stream.of(Environment.MVND_EXT_CLASSPATH.asString().split(","))
.filter(s -> s != null && !s.isEmpty())
.map(Paths::get)
.toList();
}
}

View File

@@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.maven.cling.invoker.mvn.LayeredMavenOptions;
public class LayeredDaemonMavenOptions extends LayeredMavenOptions<DaemonMavenOptions> implements DaemonMavenOptions {
public static DaemonMavenOptions layerDaemonMavenOptions(Collection<DaemonMavenOptions> options) {
List<DaemonMavenOptions> o = options.stream().filter(Objects::nonNull).toList();
if (o.isEmpty()) {
throw new IllegalArgumentException("No options specified (or all were null)");
} else if (o.size() == 1) {
return o.get(0);
} else {
return new LayeredDaemonMavenOptions(o);
}
}
private LayeredDaemonMavenOptions(List<DaemonMavenOptions> options) {
super(options);
}
@Override
public Optional<Boolean> rawStreams() {
return returnFirstPresentOrEmpty(DaemonMavenOptions::rawStreams);
}
@Override
public DaemonMavenOptions interpolate(Collection<Map<String, String>> properties) {
ArrayList<DaemonMavenOptions> interpolatedOptions = new ArrayList<>(options.size());
for (DaemonMavenOptions o : options) {
interpolatedOptions.add(o.interpolate(properties));
}
return layerDaemonMavenOptions(interpolatedOptions);
}
}

View File

@@ -21,7 +21,6 @@ package org.apache.maven.cli;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Set;
@@ -48,13 +47,13 @@ public class MvndHelpFormatter {
/**
* Returns Maven option descriptions combined with mvnd options descriptions
*
* @param cliManager
* @param cliManager The cli manager
* @return the string containing the help message
*/
public static String displayHelp(CLIManager cliManager) {
public static String displayHelp(CommonsCliDaemonMavenOptions cliManager) {
int terminalWidth = getTerminalWidth();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream out = new PrintStream(baos, false, StandardCharsets.UTF_8.name())) {
try (PrintStream out = new PrintStream(baos, false, StandardCharsets.UTF_8)) {
out.println();
PrintWriter pw = new PrintWriter(out);
HelpFormatter formatter = new HelpFormatter();
@@ -63,16 +62,14 @@ public class MvndHelpFormatter {
terminalWidth,
"mvnd [options] [<goal(s)>] [<phase(s)>]",
"\nOptions:",
cliManager.options,
cliManager.getOptions(),
1,
3,
"\n",
false);
pw.flush();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
final String mvnHelp = new String(baos.toByteArray(), StandardCharsets.UTF_8);
final String mvnHelp = baos.toString(StandardCharsets.UTF_8);
final Matcher m = COLUMNS_DETECTOR_PATTERN.matcher(mvnHelp);
final String indent = m.find() ? m.group() : " ";
@@ -201,12 +198,8 @@ public class MvndHelpFormatter {
*
* @param stringBuilder the {@link StringBuilder} to append to
* @param count the number of spaces to append
* @return the given {@code stringBuilder}
*/
static StringBuilder spaces(StringBuilder stringBuilder, int count) {
for (int i = 0; i < count; i++) {
stringBuilder.append(' ');
}
return stringBuilder;
static void spaces(StringBuilder stringBuilder, int count) {
stringBuilder.append(" ".repeat(Math.max(0, count)));
}
}

View File

@@ -131,7 +131,7 @@ public class Server implements AutoCloseable, Runnable {
try {
cli = (DaemonCli) getClass()
.getClassLoader()
.loadClass("org.apache.maven.cli.DaemonMavenCli")
.loadClass("org.apache.maven.cli.DaemonMavenCling")
.getDeclaredConstructor()
.newInstance();
registry = new DaemonRegistry(Environment.MVND_REGISTRY.asPath());
@@ -187,7 +187,11 @@ public class Server implements AutoCloseable, Runnable {
try {
registry.close();
} finally {
socket.close();
try {
socket.close();
} finally {
cli.close();
}
}
}
}