Fix #56 Require Java 8+ instead of Java 11+ at runtime

This commit is contained in:
Peter Palaga
2020-08-14 17:48:54 +02:00
parent d780eb0f57
commit 6ac2a01c64
37 changed files with 260 additions and 157 deletions

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.nio.Buffer;
/**
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/base-services/src/main/java/org/gradle/internal/io/BufferCaster.java
*/
public class BufferCaster {
/**
* Without this cast, when the code compiled by Java 9+ is executed on Java 8, it will throw
* java.lang.NoSuchMethodError: Method flip()Ljava/nio/ByteBuffer; does not exist in class java.nio.ByteBuffer
*/
@SuppressWarnings("RedundantCast")
public static <T extends Buffer> Buffer cast(T byteBuffer) {
return (Buffer) byteBuffer;
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BuildProperties {
private static final BuildProperties INSTANCE = load();
public static BuildProperties getInstance() {
return INSTANCE;
}
public static BuildProperties load() {
final Properties buildProperties = new Properties();
try (InputStream is = BuildProperties.class.getResourceAsStream("build.properties")) {
buildProperties.load(is);
} catch (IOException e) {
throw new RuntimeException("Could not read build.properties");
}
return new BuildProperties(buildProperties.getProperty("version"));
}
private final String version;
public BuildProperties(String version) {
this.version = version;
}
public String getVersion() {
return version;
}
}

View File

@@ -15,11 +15,17 @@
*/
package org.jboss.fuse.mvnd.client;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.function.Supplier;
import org.jboss.fuse.mvnd.common.BuildProperties;
import org.jboss.fuse.mvnd.common.Environment;
import org.jboss.fuse.mvnd.common.Environment.ValueSource;
import org.jboss.fuse.mvnd.common.Layout;
/**
* Local paths relevant for the {@link DefaultClient}.
@@ -38,7 +44,28 @@ public class ClientLayout extends Layout {
final Path mvndPropertiesPath = Environment.findMvndPropertiesPath();
final Supplier<Properties> mvndProperties = lazyMvndProperties(mvndPropertiesPath);
final Path pwd = Paths.get(".").toAbsolutePath().normalize();
final Path mvndHome = Environment.findMavenHome(mvndProperties, mvndPropertiesPath);
final Path mvndHome = Environment.findBasicMavenHome()
.orLocalProperty(mvndProperties, mvndPropertiesPath)
.or(new ValueSource(
description -> description.append("path relative to the mvnd executable"),
() -> {
Optional<String> cmd = ProcessHandle.current().info().command();
if (Environment.isNative() && cmd.isPresent()) {
final Path mvndH = Paths.get(cmd.get()).getParent().getParent();
if (mvndH != null) {
final Path mvndDaemonLib = mvndH
.resolve("lib/ext/mvnd-daemon-" + BuildProperties.getInstance().getVersion()
+ ".jar");
if (Files.exists(mvndDaemonLib)) {
return mvndH.toString();
}
}
}
return null;
}))
.orFail()
.asPath()
.toAbsolutePath().normalize();
ENV_INSTANCE = new ClientLayout(
mvndPropertiesPath,
mvndHome,

View File

@@ -30,7 +30,7 @@ import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.function.Consumer;
import org.jboss.fuse.mvnd.client.Message.BuildException;
import org.jboss.fuse.mvnd.common.Message.BuildException;
import org.jline.terminal.Size;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

View File

@@ -17,6 +17,13 @@ package org.jboss.fuse.mvnd.client;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jboss.fuse.mvnd.common.DaemonConnection;
import org.jboss.fuse.mvnd.common.DaemonException;
import org.jboss.fuse.mvnd.common.DaemonException.ConnectException;
import org.jboss.fuse.mvnd.common.DaemonException.MessageIOException;
import org.jboss.fuse.mvnd.common.DaemonException.StaleAddressException;
import org.jboss.fuse.mvnd.common.DaemonInfo;
import org.jboss.fuse.mvnd.common.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -83,7 +90,7 @@ public class DaemonClientConnection {
connection.close();
}
interface StaleAddressDetector {
public interface StaleAddressDetector {
/**
* @return true if the failure should be considered due to a stale address.
*/

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
/**
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonCompatibilitySpec.java
*/
public class DaemonCompatibilitySpec {
private final Path javaHome;
private final List<String> options;
/**
* @param javaHome make sure the Path is a result of {@link Path#toRealPath(java.nio.file.LinkOption...)}
* @param options the options
*/
public DaemonCompatibilitySpec(Path javaHome, List<String> options) {
this.javaHome = Objects.requireNonNull(javaHome, "javaHome");
this.options = Objects.requireNonNull(options, "options");
}
public Result isSatisfiedBy(DaemonInfo daemon) {
if (!javaHomeMatches(daemon)) {
return new Result(false, () -> "Java home is different.\n" + diff(daemon));
}
if (!daemonOptsMatch(daemon)) {
return new Result(false, () -> "At least one daemon option is different.\n" + diff(daemon));
}
return new Result(true, () -> {
throw new RuntimeException("No reason if DaemonCompatibilityResult.compatible == true");
});
}
private String diff(DaemonInfo context) {
final StringBuilder sb = new StringBuilder("Wanted: ");
appendFields(sb);
sb.append("\nActual: ");
context.appendNonKeyFields(sb).append("uid=").append(context.getUid()).append('\n');
return sb.toString();
}
private boolean daemonOptsMatch(DaemonInfo daemon) {
return daemon.getOptions().containsAll(options)
&& daemon.getOptions().size() == options.size();
}
private boolean javaHomeMatches(DaemonInfo daemon) {
return javaHome.equals(Paths.get(daemon.getJavaHome()));
}
StringBuilder appendFields(StringBuilder sb) {
return sb.append("javaHome=").append(javaHome)
.append(", options=").append(options);
}
@Override
public String toString() {
return appendFields(new StringBuilder("DaemonCompatibilitySpec{")).append('}').toString();
}
public static class Result {
private final boolean compatible;
private final Supplier<String> why;
Result(boolean compatible, Supplier<String> why) {
super();
this.compatible = compatible;
this.why = why;
}
public boolean isCompatible() {
return compatible;
}
public String getWhy() {
return why.get();
}
}
}

View File

@@ -1,309 +0,0 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/messaging/src/main/java/org/gradle/internal/remote/internal/inet/SocketConnection.java
*
*/
public class DaemonConnection<T> implements AutoCloseable {
private static final Logger LOGGER = LoggerFactory.getLogger(DaemonConnection.class);
private final SocketChannel socket;
private final Serializer<T> serializer;
private final InetSocketAddress localAddress;
private final InetSocketAddress remoteAddress;
private final DataInputStream instr;
private final DataOutputStream outstr;
public DaemonConnection(SocketChannel socket, Serializer<T> serializer) {
this.socket = socket;
this.serializer = serializer;
try {
// NOTE: we use non-blocking IO as there is no reliable way when using blocking IO to shutdown reads while
// keeping writes active. For example, Socket.shutdownInput() does not work on Windows.
socket.configureBlocking(false);
outstr = new DataOutputStream(new SocketOutputStream(socket));
instr = new DataInputStream(new SocketInputStream(socket));
} catch (IOException e) {
throw new DaemonException.InterruptedException(e);
}
localAddress = (InetSocketAddress) socket.socket().getLocalSocketAddress();
remoteAddress = (InetSocketAddress) socket.socket().getRemoteSocketAddress();
}
@Override
public String toString() {
return "socket connection from " + localAddress + " to " + remoteAddress;
}
public T receive() throws DaemonException.MessageIOException {
try {
return serializer.read(instr);
} catch (EOFException e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Discarding EOFException: {}", e.toString());
}
return null;
} catch (ClassNotFoundException | IOException e) {
throw new DaemonException.RecoverableMessageIOException(
String.format("Could not read message from '%s'.", remoteAddress), e);
} catch (Throwable e) {
throw new DaemonException.MessageIOException(String.format("Could not read message from '%s'.", remoteAddress), e);
}
}
private static boolean isEndOfStream(Exception e) {
if (e instanceof EOFException) {
return true;
}
if (e instanceof IOException) {
if (Objects.equals(e.getMessage(), "An existing connection was forcibly closed by the remote host")) {
return true;
}
if (Objects.equals(e.getMessage(), "An established connection was aborted by the software in your host machine")) {
return true;
}
if (Objects.equals(e.getMessage(), "Connection reset by peer")) {
return true;
}
if (Objects.equals(e.getMessage(), "Connection reset")) {
return true;
}
}
return false;
}
public void dispatch(T message) throws DaemonException.MessageIOException {
try {
serializer.write(outstr, message);
outstr.flush();
} catch (ClassNotFoundException | IOException e) {
throw new DaemonException.RecoverableMessageIOException(
String.format("Could not write message %s to '%s'.", message, remoteAddress), e);
} catch (Throwable e) {
throw new DaemonException.MessageIOException(
String.format("Could not write message %s to '%s'.", message, remoteAddress), e);
}
}
public void flush() throws DaemonException.MessageIOException {
try {
outstr.flush();
} catch (Throwable e) {
throw new DaemonException.MessageIOException(String.format("Could not write '%s'.", remoteAddress), e);
}
}
public void close() {
Throwable failure = null;
List<Closeable> elements = Arrays.asList(this::flush, instr, outstr, socket);
for (Closeable element : elements) {
try {
element.close();
} catch (Throwable throwable) {
if (failure == null) {
failure = throwable;
} else if (!Thread.currentThread().isInterrupted()) {
LOGGER.error(String.format("Could not stop %s.", element), throwable);
}
}
}
if (failure != null) {
throw new DaemonException(failure);
}
}
private static class SocketInputStream extends InputStream {
private final Selector selector;
private final ByteBuffer buffer;
private final SocketChannel socket;
private final byte[] readBuffer = new byte[1];
public SocketInputStream(SocketChannel socket) throws IOException {
this.socket = socket;
selector = Selector.open();
socket.register(selector, SelectionKey.OP_READ);
buffer = ByteBuffer.allocateDirect(4096);
BufferCaster.cast(buffer).limit(0);
}
@Override
public int read() throws IOException {
int nread = read(readBuffer, 0, 1);
if (nread <= 0) {
return nread;
}
return readBuffer[0] & 0xFF;
}
@Override
public int read(byte[] dest, int offset, int max) throws IOException {
if (max == 0) {
return 0;
}
if (buffer.remaining() == 0) {
try {
selector.select();
} catch (ClosedSelectorException e) {
return -1;
}
if (!selector.isOpen()) {
return -1;
}
BufferCaster.cast(buffer).clear();
int nread;
try {
nread = socket.read(buffer);
} catch (IOException e) {
if (isEndOfStream(e)) {
BufferCaster.cast(buffer).position(0);
BufferCaster.cast(buffer).limit(0);
return -1;
}
throw e;
}
BufferCaster.cast(buffer).flip();
if (nread < 0) {
return -1;
}
}
int count = Math.min(buffer.remaining(), max);
buffer.get(dest, offset, count);
return count;
}
@Override
public void close() throws IOException {
selector.close();
}
}
private static class SocketOutputStream extends OutputStream {
private static final int RETRIES_WHEN_BUFFER_FULL = 2;
private Selector selector;
private final SocketChannel socket;
private final ByteBuffer buffer;
private final byte[] writeBuffer = new byte[1];
public SocketOutputStream(SocketChannel socket) throws IOException {
this.socket = socket;
buffer = ByteBuffer.allocateDirect(32 * 1024);
}
@Override
public void write(int b) throws IOException {
writeBuffer[0] = (byte) b;
write(writeBuffer);
}
@Override
public void write(byte[] src, int offset, int max) throws IOException {
int remaining = max;
int currentPos = offset;
while (remaining > 0) {
int count = Math.min(remaining, buffer.remaining());
if (count > 0) {
buffer.put(src, currentPos, count);
remaining -= count;
currentPos += count;
}
while (buffer.remaining() == 0) {
writeBufferToChannel();
}
}
}
@Override
public void flush() throws IOException {
while (buffer.position() > 0) {
writeBufferToChannel();
}
}
private void writeBufferToChannel() throws IOException {
BufferCaster.cast(buffer).flip();
int count = writeWithNonBlockingRetry();
if (count == 0) {
// buffer was still full after non-blocking retries, now block
waitForWriteBufferToDrain();
}
buffer.compact();
}
private int writeWithNonBlockingRetry() throws IOException {
int count = 0;
int retryCount = 0;
while (count == 0 && retryCount++ < RETRIES_WHEN_BUFFER_FULL) {
count = socket.write(buffer);
if (count < 0) {
throw new EOFException();
} else if (count == 0) {
// buffer was full, just call Thread.yield
Thread.yield();
}
}
return count;
}
private void waitForWriteBufferToDrain() throws IOException {
if (selector == null) {
selector = Selector.open();
}
SelectionKey key = socket.register(selector, SelectionKey.OP_WRITE);
// block until ready for write operations
selector.select();
// cancel OP_WRITE selection
key.cancel();
// complete cancelling key
selector.selectNow();
}
@Override
public void close() throws IOException {
if (selector != null) {
selector.close();
selector = null;
}
}
}
}

View File

@@ -30,14 +30,27 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.jboss.fuse.mvnd.client.DaemonCompatibilitySpec.Result;
import org.jboss.fuse.mvnd.common.BuildProperties;
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec;
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec.Result;
import org.jboss.fuse.mvnd.common.DaemonConnection;
import org.jboss.fuse.mvnd.common.DaemonDiagnostics;
import org.jboss.fuse.mvnd.common.DaemonException;
import org.jboss.fuse.mvnd.common.DaemonInfo;
import org.jboss.fuse.mvnd.common.DaemonRegistry;
import org.jboss.fuse.mvnd.common.DaemonState;
import org.jboss.fuse.mvnd.common.DaemonStopEvent;
import org.jboss.fuse.mvnd.common.Environment;
import org.jboss.fuse.mvnd.common.Message;
import org.jboss.fuse.mvnd.common.Serializer;
import org.jboss.fuse.mvnd.common.ServerMain;
import org.jboss.fuse.mvnd.jpm.Process;
import org.jboss.fuse.mvnd.jpm.ScriptUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.lang.Thread.sleep;
import static org.jboss.fuse.mvnd.client.DaemonState.Canceled;
import static org.jboss.fuse.mvnd.common.DaemonState.Canceled;
/**
* File origin:
@@ -238,7 +251,7 @@ public class DaemonConnector {
final Path workingDir = layout.userDir();
String command = "";
try {
String classpath = findClientJar(mavenHome).toString();
String classpath = findCommonJar(mavenHome).toString();
final String java = ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java";
List<String> args = new ArrayList<>();
args.add("\"" + layout.javaHome().resolve(java) + "\"");
@@ -270,8 +283,8 @@ public class DaemonConnector {
}
}
private Path findClientJar(Path mavenHome) {
final Path result = mavenHome.resolve("lib/ext/mvnd-client-" + buildProperties.getVersion() + ".jar");
private Path findCommonJar(Path mavenHome) {
final Path result = mavenHome.resolve("lib/ext/mvnd-common-" + buildProperties.getVersion() + ".jar");
if (!Files.isRegularFile(result)) {
throw new RuntimeException("File must exist and must be a regular file: " + result);
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.stream.Collector;
/**
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/diagnostics/DaemonDiagnostics.java
*/
public class DaemonDiagnostics {
private final static int TAIL_SIZE = 20;
private final String uid;
private final Path daemonLog;
public DaemonDiagnostics(String uid, Path daemonLog) {
this.uid = uid;
this.daemonLog = daemonLog;
}
@Override
public String toString() {
return "{"
+ "uid=" + uid
+ ", daemonLog=" + daemonLog
+ '}';
}
private String tailDaemonLog() {
try {
String tail = tail(daemonLog, TAIL_SIZE);
return formatTail(tail);
} catch (IOException e) {
return "Unable to read from the daemon log file: " + daemonLog + ", because of: " + e.getCause();
}
}
/**
* @param path to read from tail
* @param maxLines max lines to read
* @return tail content
* @throws IOException when reading failed
*/
static String tail(Path path, int maxLines) throws IOException {
try (BufferedReader r = Files.newBufferedReader(path)) {
return String.join("\n", r.lines().collect(lastN(maxLines)));
}
}
static <T> Collector<T, ?, List<T>> lastN(int n) {
return Collector.<T, Deque<T>, List<T>> of(ArrayDeque::new, (acc, t) -> {
if (acc.size() == n)
acc.pollFirst();
acc.add(t);
}, (acc1, acc2) -> {
while (acc2.size() < n && !acc1.isEmpty()) {
acc2.addFirst(acc1.pollLast());
}
return acc2;
}, ArrayList::new);
}
private String formatTail(String tail) {
return "----- Last " + TAIL_SIZE + " lines from daemon log file - " + daemonLog + " -----\n"
+ tail
+ "----- End of the daemon log -----\n";
}
public String describe() {
return "Daemon uid: " + uid + "\n"
+ " log file: " + daemonLog + "\n"
+ tailDaemonLog();
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
public class DaemonException extends RuntimeException {
public DaemonException(String message) {
super(message);
}
public DaemonException(String message, Throwable cause) {
super(message, cause);
}
public DaemonException(Throwable cause) {
super(cause);
}
public static class InterruptedException extends DaemonException {
public InterruptedException(Throwable cause) {
super(cause);
}
}
public static class ConnectException extends DaemonException {
public ConnectException(String message) {
super(message);
}
public ConnectException(String message, Throwable cause) {
super(message, cause);
}
}
public static class StartException extends DaemonException {
public StartException(String message) {
super(message);
}
public StartException(String message, Throwable cause) {
super(message, cause);
}
}
public static class MessageIOException extends DaemonException {
public MessageIOException(String message) {
super(message);
}
public MessageIOException(String message, Throwable cause) {
super(message, cause);
}
}
public static class RecoverableMessageIOException extends MessageIOException {
public RecoverableMessageIOException(String message) {
super(message);
}
public RecoverableMessageIOException(String message, Throwable cause) {
super(message, cause);
}
}
public static class StaleAddressException extends DaemonException {
public StaleAddressException(String message) {
super(message);
}
public StaleAddressException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
/**
* Expiration status for daemon expiration check results.
* Note that order here is important, higher ordinal statuses
* take precedent over lower ordinal statuses when aggregating
* results.
*
* File origin:
* https://github.com/gradle/gradle/blob/v6.5.1/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/expiry/DaemonExpirationStatus.java
*/
public enum DaemonExpirationStatus {
DO_NOT_EXPIRE,
QUIET_EXPIRE,
GRACEFUL_EXPIRE,
IMMEDIATE_EXPIRE;
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.util.List;
import static org.jboss.fuse.mvnd.client.DaemonState.Busy;
import static org.jboss.fuse.mvnd.client.DaemonState.Idle;
/**
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonInfo.java
*/
public class DaemonInfo {
private final String uid;
private final String javaHome;
private final String mavenHome;
private final int pid;
private final int address;
private final int idleTimeout;
private final String locale;
private final List<String> options;
private final DaemonState state;
private final long lastIdle;
private final long lastBusy;
public DaemonInfo(String uid, String javaHome, String mavenHome,
int pid, int address, int idleTimeout,
String locale, List<String> options,
DaemonState state, long lastIdle, long lastBusy) {
this.uid = uid;
this.javaHome = javaHome;
this.mavenHome = mavenHome;
this.pid = pid;
this.address = address;
this.idleTimeout = idleTimeout;
this.locale = locale;
this.options = options;
this.state = state;
this.lastIdle = lastIdle;
this.lastBusy = lastBusy;
}
public String getUid() {
return uid;
}
public String getJavaHome() {
return javaHome;
}
public String getMavenHome() {
return mavenHome;
}
public int getPid() {
return pid;
}
public int getAddress() {
return address;
}
public int getIdleTimeout() {
return idleTimeout;
}
public String getLocale() {
return locale;
}
public List<String> getOptions() {
return options;
}
public DaemonState getState() {
return state;
}
public long getLastIdle() {
return lastIdle;
}
public long getLastBusy() {
return lastBusy;
}
public DaemonInfo withState(DaemonState state) {
long lb, li;
if (this.state == Idle && state == Busy) {
li = lastIdle;
lb = System.currentTimeMillis();
} else if (this.state == Busy && state == Idle) {
li = System.currentTimeMillis();
lb = lastBusy;
} else {
li = lastIdle;
lb = lastBusy;
}
return new DaemonInfo(uid, javaHome, mavenHome, pid, address,
idleTimeout, locale, options, state, li, lb);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("DaemonInfo{uid=").append(uid);
appendNonKeyFields(sb);
return sb.append('}').toString();
}
public StringBuilder appendNonKeyFields(StringBuilder sb) {
return sb.append("javaHome=").append(javaHome)
.append(", options=").append(options)
.append(", mavenHome=").append(mavenHome)
.append(", pid=").append(pid)
.append(", address=").append(address)
.append(", idleTimeout=").append(idleTimeout)
.append(", locale=").append(locale)
.append(", state=").append(state)
.append(", lastIdle=").append(lastIdle)
.append(", lastBusy=").append(lastBusy);
}
}

View File

@@ -1,538 +0,0 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;
import static org.jboss.fuse.mvnd.client.DaemonState.Canceled;
import static org.jboss.fuse.mvnd.client.DaemonState.Idle;
/**
* Access to daemon registry files. Useful also for testing.
*
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonRegistry.java
*/
public class DaemonRegistry implements AutoCloseable {
private static final Logger LOGGER = LoggerFactory.getLogger(DaemonRegistry.class);
private static final int MAX_LENGTH = 32768;
private final Path registryFile;
private final Lock lock = new ReentrantLock();
private final FileChannel channel;
private final MappedByteBuffer buffer;
private long seq;
private final Map<String, DaemonInfo> infosMap = new HashMap<>();
private final List<DaemonStopEvent> stopEvents = new ArrayList<>();
public DaemonRegistry(Path registryFile) {
this.registryFile = registryFile;
try {
if (!Files.isRegularFile(registryFile)) {
if (!Files.isDirectory(registryFile.getParent())) {
Files.createDirectories(registryFile.getParent());
}
}
channel = FileChannel.open(registryFile,
StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, MAX_LENGTH);
} catch (IOException e) {
throw new DaemonException(e);
}
}
public void close() {
try {
channel.close();
} catch (IOException e) {
throw new DaemonException("Error closing registry", e);
}
}
public Path getRegistryFile() {
return registryFile;
}
public DaemonInfo get(String uid) {
lock.lock();
try {
read();
return infosMap.get(uid);
} finally {
lock.unlock();
}
}
public List<DaemonInfo> getAll() {
lock.lock();
try {
read();
return new ArrayList<>(infosMap.values());
} finally {
lock.unlock();
}
}
public List<DaemonInfo> getIdle() {
lock.lock();
try {
read();
return infosMap.values().stream()
.filter(di -> di.getState() == Idle)
.collect(Collectors.toList());
} finally {
lock.unlock();
}
}
public List<DaemonInfo> getNotIdle() {
lock.lock();
try {
read();
return infosMap.values().stream()
.filter(di -> di.getState() != Idle)
.collect(Collectors.toList());
} finally {
lock.unlock();
}
}
public List<DaemonInfo> getCanceled() {
lock.lock();
try {
read();
return infosMap.values().stream()
.filter(di -> di.getState() == Canceled)
.collect(Collectors.toList());
} finally {
lock.unlock();
}
}
public void remove(final String uid) {
lock.lock();
LOGGER.debug("Removing daemon uid: {}", uid);
try {
update(() -> infosMap.remove(uid));
} finally {
lock.unlock();
}
}
public void markState(final String uid, final DaemonState state) {
lock.lock();
LOGGER.debug("Marking busy by uid: {}", uid);
try {
update(() -> infosMap.computeIfPresent(uid, (id, di) -> di.withState(state)));
} finally {
lock.unlock();
}
}
public void storeStopEvent(final DaemonStopEvent stopEvent) {
lock.lock();
LOGGER.debug("Storing daemon stop event with timestamp {}", stopEvent.getTimestamp());
try {
update(() -> stopEvents.add(stopEvent));
} finally {
lock.unlock();
}
}
public List<DaemonStopEvent> getStopEvents() {
lock.lock();
LOGGER.debug("Getting daemon stop events");
try {
read();
return new ArrayList<>(stopEvents);
} finally {
lock.unlock();
}
}
public void removeStopEvents(final Collection<DaemonStopEvent> events) {
lock.lock();
LOGGER.info("Removing {} daemon stop events from registry", events.size());
try {
update(() -> stopEvents.removeAll(events));
} finally {
lock.unlock();
}
}
public void store(final DaemonInfo info) {
lock.lock();
LOGGER.debug("Storing daemon {}", info);
try {
update(() -> infosMap.put(info.getUid(), info));
} finally {
lock.unlock();
}
}
private static final long OFFSET_LOCK = 0;
private static final long OFFSET_SEQ = OFFSET_LOCK + Long.BYTES;
private static final long OFFSET_DATA = OFFSET_SEQ + Long.BYTES;
private void read() {
doUpdate(null);
}
private void update(Runnable updater) {
doUpdate(updater);
}
private void doUpdate(Runnable updater) {
if (!Files.isReadable(getRegistryFile())) {
throw new DaemonException("Registry became unaccessible");
}
try {
busyLockLong(OFFSET_LOCK);
try {
long newSeq = readLong(OFFSET_SEQ);
if (newSeq != seq) {
seq = newSeq;
BufferCaster.cast(buffer).position((int) OFFSET_DATA);
infosMap.clear();
int nb = buffer.getInt();
for (int i = 0; i < nb; i++) {
String uid = readString();
String javaHome = readString();
String mavenHome = readString();
int pid = buffer.getInt();
int address = buffer.getInt();
int idle = buffer.getInt();
String locale = readString();
List<String> opts = new ArrayList<>();
int nbOpts = buffer.getInt();
for (int j = 0; j < nbOpts; j++) {
opts.add(readString());
}
DaemonState state = DaemonState.values()[buffer.get()];
long lastIdle = buffer.getLong();
long lastBusy = buffer.getLong();
DaemonInfo di = new DaemonInfo(uid, javaHome, mavenHome, pid, address, idle, locale, opts, state,
lastIdle, lastBusy);
infosMap.putIfAbsent(di.getUid(), di);
}
stopEvents.clear();
nb = buffer.getInt();
for (int i = 0; i < nb; i++) {
String uid = readString();
long date = buffer.getLong();
int ord = buffer.get();
DaemonExpirationStatus des = ord >= 0 ? DaemonExpirationStatus.values()[ord] : null;
String reason = readString();
DaemonStopEvent se = new DaemonStopEvent(uid, date, des, reason);
stopEvents.add(se);
}
}
if (updater != null) {
updater.run();
writeLong(OFFSET_SEQ, ++seq);
BufferCaster.cast(buffer).position((int) OFFSET_DATA);
buffer.putInt(infosMap.size());
for (DaemonInfo di : infosMap.values()) {
writeString(di.getUid());
writeString(di.getJavaHome());
writeString(di.getMavenHome());
buffer.putInt(di.getPid());
buffer.putInt(di.getAddress());
buffer.putInt(di.getIdleTimeout());
writeString(di.getLocale());
buffer.putInt(di.getOptions().size());
for (String opt : di.getOptions()) {
writeString(opt);
}
buffer.put((byte) di.getState().ordinal());
buffer.putLong(di.getLastIdle());
buffer.putLong(di.getLastBusy());
}
buffer.putInt(stopEvents.size());
for (DaemonStopEvent dse : stopEvents) {
writeString(dse.getUid());
buffer.putLong(dse.getTimestamp());
buffer.put((byte) (dse.getStatus() == null ? -1 : dse.getStatus().ordinal()));
writeString(dse.getReason());
}
}
} finally {
unlockLong(OFFSET_LOCK);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private static final int PROCESS_ID = getProcessId0();
private static int getProcessId0() {
String pid = null;
final File self = new File("/proc/self");
try {
if (self.exists()) {
pid = self.getCanonicalFile().getName();
}
} catch (IOException ignored) {
}
if (pid == null) {
pid = ManagementFactory.getRuntimeMXBean().getName().split("@", 0)[0];
}
if (pid == null) {
int rpid = new Random().nextInt(1 << 16);
LOGGER.warn("Unable to determine PID, picked a random number=" + rpid);
return rpid;
} else {
return Integer.parseInt(pid);
}
}
private static long uniqueTid() {
// Assume 48 bit for 16 to 24-bit process id and 16 million threads from the start.
return ((long) getProcessId() << 24) | currentThread().getId();
}
public static int getProcessId() {
return PROCESS_ID;
}
private static Thread currentThread() {
return Thread.currentThread();
}
static final int SLEEP_THRESHOLD = 20 * 1000 * 1000;
static final long BUSY_LOCK_LIMIT = 20L * 1000 * 1000 * 1000;
public void busyLockLong(long offset) throws InterruptedException, IllegalStateException {
boolean success = tryLockNanosLong(offset, BUSY_LOCK_LIMIT);
if (!success)
if (currentThread().isInterrupted())
throw new InterruptedException();
else
throw new IllegalStateException("Failed to acquire lock after " + BUSY_LOCK_LIMIT / 1e9 + " seconds.");
}
public void unlockLong(long offset) throws IllegalMonitorStateException {
long id = uniqueTid();
long firstValue = (1L << 48) | id;
if (compareAndSwapLong(offset, firstValue, 0))
return;
// try to check the lowId and the count.
unlockFailedLong(offset, id);
}
public void resetLockLong(long offset) {
writeOrderedLong(offset, 0L);
}
public boolean tryLockLong(long offset) {
long id = uniqueTid();
return tryLockNanos8a(offset, id);
}
public boolean tryLockNanosLong(long offset, long nanos) {
long id = uniqueTid();
int limit = nanos <= 10000 ? (int) nanos / 10 : 1000;
for (int i = 0; i < limit; i++)
if (tryLockNanos8a(offset, id))
return true;
if (nanos <= 10000)
return false;
return tryLockNanosLong0(offset, nanos, id);
}
private boolean tryLockNanosLong0(long offset, long nanos, long id) {
long nanos0 = Math.min(nanos, SLEEP_THRESHOLD);
long start = System.nanoTime();
long end0 = start + nanos0 - 10000;
do {
if (tryLockNanos8a(offset, id))
return true;
} while (end0 > System.nanoTime() && !currentThread().isInterrupted());
long end = start + nanos - SLEEP_THRESHOLD;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(currentThread().getName() + ", waiting for lock");
}
try {
do {
if (tryLockNanos8a(offset, id)) {
long millis = (System.nanoTime() - start) / 1000000;
if (millis > 200) {
LOGGER.warn(currentThread().getName() +
", to obtain a lock took " +
millis / 1e3 + " seconds");
}
return true;
}
Thread.sleep(1);
} while (end > System.nanoTime());
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
return false;
}
private boolean tryLockNanos8a(long offset, long id) {
long firstValue = (1L << 48) | id;
if (compareAndSwapLong(offset, 0, firstValue))
return true;
long currentValue = readLong(offset);
long lockedId = currentValue & ((1L << 48) - 1);
if (lockedId == 0) {
int count = (int) (currentValue >>> 48);
if (count != 0)
LOGGER.warn("Lock held by threadId 0 !?");
return compareAndSwapLong(offset, currentValue, firstValue);
}
if (lockedId == id) {
if (currentValue >>> 48 == 65535)
throw new IllegalStateException("Reentered 65535 times without an unlock");
currentValue += 1L << 48;
writeOrderedLong(offset, currentValue);
return true;
}
return false;
}
private void unlockFailedLong(long offset, long id) throws IllegalMonitorStateException {
long currentValue = readLong(offset);
long holderId = currentValue & (-1L >>> 16);
if (holderId == id) {
currentValue -= 1L << 48;
writeOrderedLong(offset, currentValue);
} else if (currentValue == 0) {
throw new IllegalMonitorStateException("No thread holds this lock");
} else {
throw new IllegalMonitorStateException("Process " + ((currentValue >>> 32) & 0xFFFF)
+ " thread " + (holderId & (-1L >>> 32))
+ " holds this lock, " + (currentValue >>> 48)
+ " times, unlock from " + getProcessId()
+ " thread " + currentThread().getId());
}
}
static final Unsafe UNSAFE;
static final int BYTES_OFFSET;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
BYTES_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
} catch (Exception e) {
throw new AssertionError(e);
}
}
public boolean compareAndSwapLong(long offset, long expected, long x) {
if (buffer instanceof DirectBuffer)
return UNSAFE.compareAndSwapLong(null, ((DirectBuffer) buffer).address() + offset, expected, x);
return UNSAFE.compareAndSwapLong(buffer.array(), BYTES_OFFSET + offset, expected, x);
}
public long readVolatileLong(int offset) {
readBarrier();
return readLong(offset);
}
public long readLong(long offset) {
return buffer.getLong((int) offset);
}
public void writeOrderedLong(long offset, long v) {
writeLong(offset, v);
writeBarrier();
}
public void writeLong(long offset, long v) {
buffer.putLong((int) offset, v);
}
private AtomicBoolean barrier;
private void readBarrier() {
if (barrier == null)
barrier = new AtomicBoolean();
barrier.get();
}
private void writeBarrier() {
if (barrier == null)
barrier = new AtomicBoolean();
barrier.lazySet(false);
}
private String readString() {
int sz = buffer.getShort();
if (sz == -1) {
return null;
}
if (sz < -1 || sz > 1024) {
throw new IllegalStateException("Bad string size: " + sz);
}
byte[] buf = new byte[sz];
buffer.get(buf);
return new String(buf, StandardCharsets.UTF_8);
}
private void writeString(String str) {
if (str == null) {
buffer.putShort((short) -1);
} else if (str.length() > 1024) {
throw new IllegalStateException("String too long: " + str);
} else {
byte[] buf = str.getBytes(StandardCharsets.UTF_8);
buffer.putShort((short) buf.length);
buffer.put(buf);
}
}
public String toString() {
return String.format("PersistentDaemonRegistry[file=%s]", registryFile);
}
}

View File

@@ -1,22 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
public interface DaemonStarter {
String startDaemon();
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
/**
* File origin
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonStateControl.java
*/
public enum DaemonState {
Idle,
Busy,
Canceled,
StopRequested,
Stopped,
Broken
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.Serializable;
import java.text.DateFormat;
import java.util.Date;
import java.util.Objects;
/**
* Information regarding when and why a daemon was stopped.
*
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonStopEvent.java
*/
public class DaemonStopEvent implements Serializable {
private final String uid;
private final long timestamp;
private final DaemonExpirationStatus status;
private final String reason;
public DaemonStopEvent(String uid, long timestamp, DaemonExpirationStatus status, String reason) {
this.uid = uid;
this.timestamp = timestamp;
this.status = status;
this.reason = reason != null ? reason : "";
}
public String getUid() {
return uid;
}
public long getTimestamp() {
return timestamp;
}
public DaemonExpirationStatus getStatus() {
return status;
}
public String getReason() {
return reason;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
DaemonStopEvent that = (DaemonStopEvent) o;
return Objects.equals(uid, that.uid)
&& timestamp == that.timestamp
&& status == that.status
&& Objects.equals(reason, that.reason);
}
@Override
public int hashCode() {
return Objects.hash(timestamp, uid, status, reason);
}
@Override
public String toString() {
return "DaemonStopEvent{"
+ "uid=" + uid
+ ", timestamp=" + DateFormat.getDateTimeInstance().format(new Date(timestamp))
+ ", status=" + status
+ ", reason=" + reason
+ "}";
}
}

View File

@@ -24,14 +24,19 @@ import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.fusesource.jansi.Ansi;
import org.jboss.fuse.mvnd.client.ClientOutput.TerminalOutput;
import org.jboss.fuse.mvnd.client.Message.BuildEvent;
import org.jboss.fuse.mvnd.client.Message.BuildException;
import org.jboss.fuse.mvnd.client.Message.BuildMessage;
import org.jboss.fuse.mvnd.client.Message.MessageSerializer;
import org.jboss.fuse.mvnd.common.BuildProperties;
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec;
import org.jboss.fuse.mvnd.common.DaemonInfo;
import org.jboss.fuse.mvnd.common.DaemonRegistry;
import org.jboss.fuse.mvnd.common.Environment;
import org.jboss.fuse.mvnd.common.Message;
import org.jboss.fuse.mvnd.common.Message.BuildEvent;
import org.jboss.fuse.mvnd.common.Message.BuildException;
import org.jboss.fuse.mvnd.common.Message.BuildMessage;
import org.jboss.fuse.mvnd.common.Message.MessageSerializer;
import org.jboss.fuse.mvnd.jpm.ProcessImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,7 +44,6 @@ import org.slf4j.LoggerFactory;
public class DefaultClient implements Client {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClient.class);
public static final int DEFAULT_IDLE_TIMEOUT = (int) TimeUnit.HOURS.toMillis(3);
public static final int DEFAULT_PERIODIC_CHECK_INTERVAL_MILLIS = 10 * 1000;
public static final int CANCEL_TIMEOUT = 10 * 1000;
private final Supplier<ClientLayout> lazyLayout;
@@ -200,10 +204,10 @@ public class DefaultClient implements Client {
case ProjectStarted:
case MojoStarted:
case MojoStopped:
output.projectStateChanged(be.projectId, be.display);
output.projectStateChanged(be.getProjectId(), be.getDisplay());
break;
case ProjectStopped:
output.projectFinished(be.projectId);
output.projectFinished(be.getProjectId());
}
} else if (m instanceof BuildMessage) {
BuildMessage bm = (BuildMessage) m;

View File

@@ -1,313 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Collects system properties and environment variables used by mvnd client or server.
*/
public enum Environment {
LOGBACK_CONFIGURATION_FILE("logback.configurationFile", null),
JAVA_HOME("java.home", "JAVA_HOME"),
MVND_HOME("mvnd.home", "MVND_HOME"),
MAVEN_REPO_LOCAL("maven.repo.local", null),
MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null),
MVND_PROPERTIES_PATH("mvnd.properties.path", "MVND_PROPERTIES_PATH"),
MVND_DIST_URI("mvnd.dist.uri", "MVND_DIST_URI"),
DAEMON_DEBUG("daemon.debug", null),
DAEMON_IDLE_TIMEOUT("daemon.idleTimeout", null),
DAEMON_UID("daemon.uid", null);
static Properties properties = System.getProperties();
static Map<String, String> env = System.getenv();
private final String property;
private final String environmentVariable;
Environment(String property, String environmentVariable) {
this.property = property;
this.environmentVariable = environmentVariable;
}
public Environment.EnvValue systemProperty() {
return new EnvValue(this, systemPropertySource());
}
public Environment.EnvValue commandLineProperty(Supplier<Properties> commandLineProperties) {
return new EnvValue(this, new ValueSource(
description -> description.append("command line property ").append(property),
() -> commandLineProperties.get().getProperty(property)));
}
public Environment.EnvValue environmentVariable() {
return new EnvValue(this, environmentVariableSource());
}
public String asCommandLineProperty(String value) {
return "-D" + property + "=" + value;
}
public boolean hasCommandLineProperty(Collection<String> args) {
final String prefix = "-D" + property + "=";
return args.stream().anyMatch(s -> s.startsWith(prefix));
}
public static Path findJavaHome(Supplier<Properties> mvndProperties, Path mvndPropertiesPath) {
final Path result = JAVA_HOME
.environmentVariable()
.orLocalProperty(mvndProperties, mvndPropertiesPath)
.orSystemProperty()
.orFail()
.asPath();
try {
return result
.toRealPath();
} catch (IOException e) {
throw new RuntimeException("Could not get a real path from path " + result);
}
}
public static Path findMvndPropertiesPath() {
return MVND_PROPERTIES_PATH
.environmentVariable()
.orSystemProperty()
.orDefault(() -> System.getProperty("user.home") + "/.m2/mvnd.properties")
.asPath()
.toAbsolutePath().normalize();
}
public static Path findMavenHome(Supplier<Properties> mvndProperties, Path mvndPropertiesPath) {
return findBasicMavenHome()
.orLocalProperty(mvndProperties, mvndPropertiesPath)
.orFail()
.asPath()
.toAbsolutePath().normalize();
}
public static EnvValue findBasicMavenHome() {
return MVND_HOME
.environmentVariable()
.orSystemProperty()
.orRelativeMvndHome();
}
public static Path findMultiModuleProjectDirectory(Path pwd) {
return MAVEN_MULTIMODULE_PROJECT_DIRECTORY
.systemProperty()
.orDefault(() -> findDefaultMultimoduleProjectDirectory(pwd))
.asPath()
.toAbsolutePath().normalize();
}
public static String findDefaultMultimoduleProjectDirectory(Path pwd) {
Path dir = pwd;
do {
if (Files.isDirectory(dir.resolve(".mvn"))) {
return dir.toString();
}
dir = dir.getParent();
} while (dir != null);
/*
* Return pwd if .mvn directory was not found in the hierarchy.
* Maven does the same thing in mvn shell script's find_maven_basedir()
* and find_file_argument_basedir() routines
*/
return pwd.toString();
}
public static Path findLogbackConfigurationPath(Supplier<Properties> mvndProperties, Path mvndPropertiesPath,
Path mvndHome) {
return LOGBACK_CONFIGURATION_FILE
.systemProperty()
.orLocalProperty(mvndProperties, mvndPropertiesPath)
.orDefault(() -> mvndHome.resolve("conf/logging/logback.xml").toString())
.orFail()
.asPath();
}
public static boolean isNative() {
return "executable".equals(System.getProperty("org.graalvm.nativeimage.kind"));
}
private Environment.ValueSource systemPropertySource() {
if (property == null) {
throw new IllegalStateException("Cannot use " + Environment.class.getName() + " for getting a system property");
}
return new ValueSource(
description -> description.append("system property ").append(property),
() -> properties.getProperty(property));
}
private Environment.ValueSource environmentVariableSource() {
if (environmentVariable == null) {
throw new IllegalStateException(
"Cannot use " + Environment.class.getName() + "." + name() + " for getting an environment variable");
}
return new ValueSource(
description -> description.append("environment variable ").append(environmentVariable),
() -> env.get(environmentVariable));
}
/**
* A source of an environment value with a description capability.
*/
public static class ValueSource {
final Function<StringBuilder, StringBuilder> descriptionFunction;
final Supplier<String> valueSupplier;
public ValueSource(Function<StringBuilder, StringBuilder> descriptionFunction, Supplier<String> valueSupplier) {
this.descriptionFunction = descriptionFunction;
this.valueSupplier = valueSupplier;
}
/** Mostly for debugging */
@Override
public String toString() {
return descriptionFunction.apply(new StringBuilder()).toString();
}
}
/**
* A chained lazy environment value.
*/
public static class EnvValue {
private final Environment envKey;
private final Environment.ValueSource valueSource;
protected Environment.EnvValue previous;
public EnvValue(Environment envKey, Environment.ValueSource valueSource) {
this.previous = null;
this.envKey = envKey;
this.valueSource = valueSource;
}
public EnvValue(Environment.EnvValue previous, Environment envKey, Environment.ValueSource valueSource) {
this.previous = previous;
this.envKey = envKey;
this.valueSource = valueSource;
}
public Environment.EnvValue orSystemProperty() {
return new EnvValue(this, envKey, envKey.systemPropertySource());
}
public Environment.EnvValue orLocalProperty(Supplier<Properties> localProperties, Path localPropertiesPath) {
return new EnvValue(this, envKey, new ValueSource(
description -> description.append("property ").append(envKey.property).append(" in ")
.append(localPropertiesPath),
() -> localProperties.get().getProperty(envKey.property)));
}
public Environment.EnvValue orEnvironmentVariable() {
return new EnvValue(this, envKey, envKey.environmentVariableSource());
}
/**
* @return mvnd home autodetected from the location of the currently running mvnd native executable or nothing
* if we do not run in native or if {@code ProcessHandle.current().info().command()} is not available.
*/
public EnvValue orRelativeMvndHome() {
return new EnvValue(this, envKey, new ValueSource(
description -> description.append("path relative to the mvnd executable"),
() -> {
Optional<String> cmd = ProcessHandle.current().info().command();
if (isNative() && cmd.isPresent()) {
final Path mvndHome = Paths.get(cmd.get()).getParent().getParent();
if (mvndHome != null) {
final Path mvndDaemonLib = mvndHome
.resolve("lib/ext/mvnd-daemon-" + BuildProperties.getInstance().getVersion() + ".jar");
if (Files.exists(mvndDaemonLib)) {
return mvndHome.toString();
}
}
}
return null;
}));
}
public Environment.EnvValue orDefault(Supplier<String> defaultSupplier) {
return new EnvValue(this, envKey,
new ValueSource(sb -> sb.append("default").append(defaultSupplier.get()), defaultSupplier));
}
public Environment.EnvValue orFail() {
return new EnvValue(this, envKey, new ValueSource(sb -> sb, () -> {
final StringBuilder sb = new StringBuilder("Could not get value for ")
.append(Environment.class.getSimpleName())
.append(".").append(envKey.name()).append(" from any of the following sources: ");
/*
* Compose the description functions to invert the order thus getting the resolution order in the
* message
*/
Function<StringBuilder, StringBuilder> description = (s -> s);
EnvValue val = this;
while (val != null) {
description = description.compose(val.valueSource.descriptionFunction);
val = val.previous;
if (val != null) {
description = description.compose(s -> s.append(", "));
}
}
description.apply(sb);
throw new IllegalStateException(sb.toString());
}));
}
String get() {
if (previous != null) {
final String result = previous.get();
if (result != null) {
return result;
}
}
return valueSource.valueSupplier.get();
}
public String asString() {
return get();
}
public Optional<String> asOptional() {
return Optional.ofNullable(get());
}
public Path asPath() {
final String result = get();
return result == null ? null : Paths.get(result);
}
public boolean asBoolean() {
return Boolean.parseBoolean(get());
}
public int asInt() {
return Integer.parseInt(get());
}
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.function.Supplier;
public class Layout {
private static Layout ENV_INSTANCE;
private final Path mavenHome;
private final Path userDir;
private final Path multiModuleProjectDirectory;
private final Path mvndPropertiesPath;
public Layout(Path mvndPropertiesPath, Path mavenHome, Path userDir, Path multiModuleProjectDirectory) {
super();
this.mvndPropertiesPath = mvndPropertiesPath;
this.mavenHome = mavenHome;
this.userDir = userDir;
this.multiModuleProjectDirectory = multiModuleProjectDirectory;
}
public Path mavenHome() {
return mavenHome;
}
public Path userDir() {
return userDir;
}
public Path registry() {
return mavenHome.resolve("daemon/registry.bin");
}
public Path daemonLog(String daemon) {
return mavenHome.resolve("daemon/daemon-" + daemon + ".log");
}
public Path multiModuleProjectDirectory() {
return multiModuleProjectDirectory;
}
public Path getMvndPropertiesPath() {
return mvndPropertiesPath;
}
public static Layout getEnvInstance() {
if (ENV_INSTANCE == null) {
final Path mvndPropertiesPath = Environment.findMvndPropertiesPath();
final Supplier<Properties> mvndProperties = lazyMvndProperties(mvndPropertiesPath);
final Path pwd = Paths.get(".").toAbsolutePath().normalize();
ENV_INSTANCE = new Layout(
mvndPropertiesPath,
Environment.findMavenHome(mvndProperties, mvndPropertiesPath),
pwd,
Environment.findMultiModuleProjectDirectory(pwd));
}
return ENV_INSTANCE;
}
static Supplier<Properties> lazyMvndProperties(Path mvndPropertiesPath) {
return new Supplier<Properties>() {
private volatile Properties properties;
@Override
public Properties get() {
Properties result = this.properties;
if (result == null) {
result = new Properties();
if (Files.exists(mvndPropertiesPath)) {
try (InputStream in = Files.newInputStream(mvndPropertiesPath)) {
result.load(in);
} catch (IOException e) {
throw new RuntimeException("Could not read " + mvndPropertiesPath);
}
}
this.properties = result;
}
return result;
}
};
}
@Override
public String toString() {
return "Layout [mavenHome=" + mavenHome + ", userDir=" + userDir + ", multiModuleProjectDirectory="
+ multiModuleProjectDirectory + "]";
}
}

View File

@@ -1,371 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UTFDataFormatException;
import java.util.ArrayList;
import java.util.List;
public abstract class Message {
final long timestamp = System.nanoTime();
public long timestamp() {
return timestamp;
}
public static class BuildRequest extends Message {
final List<String> args;
final String workingDir;
final String projectDir;
public BuildRequest(List<String> args, String workingDir, String projectDir) {
this.args = args;
this.workingDir = workingDir;
this.projectDir = projectDir;
}
public List<String> getArgs() {
return args;
}
public String getWorkingDir() {
return workingDir;
}
public String getProjectDir() {
return projectDir;
}
@Override
public String toString() {
return "BuildRequest{" +
"args=" + args +
", workingDir='" + workingDir + '\'' +
", projectDir='" + projectDir + '\'' +
'}';
}
}
public static class BuildException extends Message {
final String message;
final String className;
final String stackTrace;
public BuildException(Throwable t) {
this(t.getMessage(), t.getClass().getName(), getStackTrace(t));
}
static String getStackTrace(Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw, true));
return sw.toString();
}
public BuildException(String message, String className, String stackTrace) {
this.message = message;
this.className = className;
this.stackTrace = stackTrace;
}
public String getMessage() {
return message;
}
public String getClassName() {
return className;
}
public String getStackTrace() {
return stackTrace;
}
@Override
public String toString() {
return "BuildException{" +
"message='" + message + '\'' +
", className='" + className + '\'' +
", stackTrace='" + stackTrace + '\'' +
'}';
}
}
public static class BuildEvent extends Message {
public enum Type {
BuildStarted, BuildStopped, ProjectStarted, ProjectStopped, MojoStarted, MojoStopped
}
final Type type;
final String projectId;
final String display;
public BuildEvent(Type type, String projectId, String display) {
this.type = type;
this.projectId = projectId;
this.display = display;
}
public Type getType() {
return type;
}
public String getProjectId() {
return projectId;
}
public String getDisplay() {
return display;
}
@Override
public String toString() {
return "BuildEvent{" +
"type=" + type +
", display='" + display + '\'' +
'}';
}
}
public static class BuildMessage extends Message {
final String message;
public BuildMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return "BuildMessage{" +
"message='" + message + '\'' +
'}';
}
}
public static class MessageSerializer implements Serializer<Message> {
final int BUILD_REQUEST = 0;
final int BUILD_EVENT = 1;
final int BUILD_MESSAGE = 2;
final int BUILD_EXCEPTION = 3;
@Override
public Message read(DataInputStream input) throws EOFException, Exception {
int type = input.read();
if (type == -1) {
return null;
}
switch (type) {
case BUILD_REQUEST:
return readBuildRequest(input);
case BUILD_EVENT:
return readBuildEvent(input);
case BUILD_MESSAGE:
return readBuildMessage(input);
case BUILD_EXCEPTION:
return readBuildException(input);
}
throw new IllegalStateException("Unexpected message type: " + type);
}
@Override
public void write(DataOutputStream output, Message value) throws Exception {
if (value instanceof BuildRequest) {
output.write(BUILD_REQUEST);
writeBuildRequest(output, (BuildRequest) value);
} else if (value instanceof BuildEvent) {
output.write(BUILD_EVENT);
writeBuildEvent(output, (BuildEvent) value);
} else if (value instanceof BuildMessage) {
output.write(BUILD_MESSAGE);
writeBuildMessage(output, (BuildMessage) value);
} else if (value instanceof BuildException) {
output.write(BUILD_EXCEPTION);
writeBuildException(output, (BuildException) value);
} else {
throw new IllegalStateException();
}
}
private BuildRequest readBuildRequest(DataInputStream input) throws IOException {
List<String> args = readStringList(input);
String workingDir = readUTF(input);
String projectDir = readUTF(input);
return new BuildRequest(args, workingDir, projectDir);
}
private void writeBuildRequest(DataOutputStream output, BuildRequest value) throws IOException {
writeStringList(output, value.args);
writeUTF(output, value.workingDir);
writeUTF(output, value.projectDir);
}
private BuildEvent readBuildEvent(DataInputStream input) throws IOException {
BuildEvent.Type type = BuildEvent.Type.values()[input.read()];
String projectId = readUTF(input);
String display = readUTF(input);
return new BuildEvent(type, projectId, display);
}
private void writeBuildEvent(DataOutputStream output, BuildEvent value) throws IOException {
output.write(value.type.ordinal());
writeUTF(output, value.projectId);
writeUTF(output, value.display);
}
private BuildMessage readBuildMessage(DataInputStream input) throws IOException {
String message = readUTF(input);
return new BuildMessage(message);
}
private void writeBuildMessage(DataOutputStream output, BuildMessage value) throws IOException {
writeUTF(output, value.message);
}
private BuildException readBuildException(DataInputStream input) throws IOException {
String message = readUTF(input);
String className = readUTF(input);
String stackTrace = readUTF(input);
return new BuildException(message, className, stackTrace);
}
private void writeBuildException(DataOutputStream output, BuildException value) throws IOException {
writeUTF(output, value.message);
writeUTF(output, value.className);
writeUTF(output, value.stackTrace);
}
private List<String> readStringList(DataInputStream input) throws IOException {
ArrayList<String> l = new ArrayList<>();
int nb = input.readInt();
for (int i = 0; i < nb; i++) {
l.add(readUTF(input));
}
return l;
}
private void writeStringList(DataOutputStream output, List<String> value) throws IOException {
output.writeInt(value.size());
for (String v : value) {
writeUTF(output, v);
}
}
private static final String INVALID_BYTE = "Invalid byte";
private static final int UTF_BUFS_CHAR_CNT = 256;
private static final int UTF_BUFS_BYTE_CNT = UTF_BUFS_CHAR_CNT * 3;
final byte[] byteBuf = new byte[UTF_BUFS_BYTE_CNT];
String readUTF(DataInputStream input) throws IOException {
int len = input.readInt();
final char[] chars = new char[len];
int i = 0, cnt = 0, charIdx = 0;
while (charIdx < len) {
if (i == cnt) {
cnt = input.read(byteBuf, 0, Math.min(UTF_BUFS_BYTE_CNT, len - charIdx));
if (cnt < 0) {
throw new EOFException();
}
i = 0;
}
final int a = byteBuf[i++] & 0xff;
if (a < 0x80) {
// low bit clear
chars[charIdx++] = (char) a;
} else if (a < 0xc0) {
throw new UTFDataFormatException(INVALID_BYTE);
} else if (a < 0xe0) {
if (i == cnt) {
cnt = input.read(byteBuf, 0, Math.min(UTF_BUFS_BYTE_CNT, len - charIdx));
if (cnt < 0) {
throw new EOFException();
}
i = 0;
}
final int b = byteBuf[i++] & 0xff;
if ((b & 0xc0) != 0x80) {
throw new UTFDataFormatException(INVALID_BYTE);
}
chars[charIdx++] = (char) ((a & 0x1f) << 6 | b & 0x3f);
} else if (a < 0xf0) {
if (i == cnt) {
cnt = input.read(byteBuf, 0, Math.min(UTF_BUFS_BYTE_CNT, len - charIdx));
if (cnt < 0) {
throw new EOFException();
}
i = 0;
}
final int b = byteBuf[i++] & 0xff;
if ((b & 0xc0) != 0x80) {
throw new UTFDataFormatException(INVALID_BYTE);
}
if (i == cnt) {
cnt = input.read(byteBuf, 0, Math.min(UTF_BUFS_BYTE_CNT, len - charIdx));
if (cnt < 0) {
throw new EOFException();
}
i = 0;
}
final int c = byteBuf[i++] & 0xff;
if ((c & 0xc0) != 0x80) {
throw new UTFDataFormatException(INVALID_BYTE);
}
chars[charIdx++] = (char) ((a & 0x0f) << 12 | (b & 0x3f) << 6 | c & 0x3f);
} else {
throw new UTFDataFormatException(INVALID_BYTE);
}
}
return String.valueOf(chars);
}
void writeUTF(DataOutputStream output, String s) throws IOException {
final int length = s.length();
output.writeInt(length);
int strIdx = 0;
int byteIdx = 0;
while (strIdx < length) {
final char c = s.charAt(strIdx++);
if (c > 0 && c <= 0x7f) {
byteBuf[byteIdx++] = (byte) c;
} else if (c <= 0x07ff) {
byteBuf[byteIdx++] = (byte) (0xc0 | 0x1f & c >> 6);
byteBuf[byteIdx++] = (byte) (0x80 | 0x3f & c);
} else {
byteBuf[byteIdx++] = (byte) (0xe0 | 0x0f & c >> 12);
byteBuf[byteIdx++] = (byte) (0x80 | 0x3f & c >> 6);
byteBuf[byteIdx++] = (byte) (0x80 | 0x3f & c);
}
if (byteIdx > UTF_BUFS_BYTE_CNT - 4) {
output.write(byteBuf, 0, byteIdx);
byteIdx = 0;
}
}
if (byteIdx > 0) {
output.write(byteBuf, 0, byteIdx);
}
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
/**
* File origin:
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Serializer.java
*/
public interface Serializer<T> {
/**
* Reads the next object from the given stream. The implementation must not perform any buffering, so that it reads only
* those bytes from the input stream that are
* required to deserialize the next object.
*
* @throws EOFException When the next object cannot be fully read due to reaching the end of stream.
*/
T read(DataInputStream input) throws EOFException, Exception;
/**
* Writes the given object to the given stream. The implementation must not perform any buffering.
*/
void write(DataOutputStream output, T value) throws Exception;
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
public class ServerMain {
public static void main(String[] args) throws Exception {
final String uidStr = Environment.DAEMON_UID.systemProperty().orFail().asString();
final Path mavenHome = Environment.MVND_HOME.systemProperty().orFail().asPath();
URL[] classpath = Stream.concat(
Stream.concat(Files.list(mavenHome.resolve("lib/ext")),
Files.list(mavenHome.resolve("lib")))
.filter(p -> p.getFileName().toString().endsWith(".jar"))
.filter(Files::isRegularFile),
Stream.of(mavenHome.resolve("conf"), mavenHome.resolve("conf/logging")))
.map(Path::normalize)
.map(Path::toUri)
.map(uri -> {
try {
return uri.toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
})
.toArray(URL[]::new);
ClassLoader loader = new URLClassLoader(classpath, null) {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
return super.findClass(name);
} catch (ClassNotFoundException e) {
return ServerMain.class.getClassLoader().loadClass(name);
}
}
};
Thread.currentThread().setContextClassLoader(loader);
Class<?> clazz = loader.loadClass("org.jboss.fuse.mvnd.daemon.Server");
try (AutoCloseable server = (AutoCloseable) clazz.getConstructor(String.class).newInstance(uidStr)) {
((Runnable) server).run();
}
}
}

View File

@@ -1,23 +0,0 @@
# 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.
buildNumber=${buildNumber}
timestamp=${timestamp}
version=${project.version}
distributionId=${distributionId}
distributionShortName=${distributionShortName}
distributionName=${distributionName}

View File

@@ -1,5 +0,0 @@
# An absolute path to your Maven Daemon installation
mvnd.home = %s
# java.home is optional if you have JAVA_HOME environment variable set
%s

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Locale;
import java.util.Random;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class DaemonRegistryTest {
@Test
public void testReadWrite() throws IOException {
Path temp = File.createTempFile("reg", ".data").toPath();
DaemonRegistry reg1 = new DaemonRegistry(temp);
DaemonRegistry reg2 = new DaemonRegistry(temp);
assertNotNull(reg1.getAll());
assertEquals(0, reg1.getAll().size());
assertNotNull(reg2.getAll());
assertEquals(0, reg2.getAll().size());
byte[] token = new byte[16];
new Random().nextBytes(token);
reg1.store(new DaemonInfo("the-uid", "/java/home/",
"/data/reg/", 0x12345678, 7502, 65536,
Locale.getDefault().toLanguageTag(), Arrays.asList("-Xmx"),
DaemonState.Idle, System.currentTimeMillis(), System.currentTimeMillis()));
assertNotNull(reg1.getAll());
assertEquals(1, reg1.getAll().size());
assertNotNull(reg2.getAll());
assertEquals(1, reg2.getAll().size());
}
}

View File

@@ -1,122 +0,0 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.jboss.fuse.mvnd.client;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class EnvironmentTest {
@Test
void prop() {
try (EnvironmentResource env = new EnvironmentResource()) {
env.props("mvnd.home", "/maven/home/prop");
Assertions.assertEquals("/maven/home/prop", Environment.MVND_HOME.systemProperty().asString());
}
}
@Test
void env() {
try (EnvironmentResource env = new EnvironmentResource()) {
env.env("MVND_HOME", "/maven/home/env");
Assertions.assertEquals("/maven/home/env", Environment.MVND_HOME.environmentVariable().asString());
}
}
@Test
void localProps() {
try (EnvironmentResource env = new EnvironmentResource()) {
final Properties localProps = new Properties();
localProps.put("mvnd.home", "/maven/home/local");
Assertions.assertEquals(Paths.get("/maven/home/local"),
Environment.MVND_HOME
.environmentVariable()
.orSystemProperty()
.orLocalProperty(() -> localProps, Paths.get("/local/properties"))
.orFail()
.asPath());
}
}
@Test
void envBeforeProp() {
try (EnvironmentResource env = new EnvironmentResource()) {
env.props("mvnd.home", "/maven/home/prop");
env.env("MVND_HOME", "/maven/home/env");
Assertions.assertEquals("/maven/home/env",
Environment.MVND_HOME
.environmentVariable()
.orSystemProperty()
.asString());
}
}
@Test
void fail() {
try (EnvironmentResource env = new EnvironmentResource()) {
try {
Assertions.assertEquals("/maven/home/env",
Environment.MVND_HOME
.environmentVariable()
.orSystemProperty()
.orFail()
.asString());
Assertions.fail("IllegalStateException expected");
} catch (IllegalStateException e) {
Assertions.assertEquals(
"Could not get value for Environment.MVND_HOME from any of the following sources: environment variable MVND_HOME, system property mvnd.home",
e.getMessage());
}
}
}
static class EnvironmentResource implements AutoCloseable {
private final Properties props = new Properties();
private final Map<String, String> env = new HashMap<>();
public EnvironmentResource() {
Environment.env = env;
Environment.properties = props;
}
public void props(String... props) {
int i = 0;
while (i < props.length) {
this.props.setProperty(props[i++], props[i++]);
}
}
public void env(String... env) {
int i = 0;
while (i < env.length) {
this.env.put(env[i++], env[i++]);
}
}
@Override
public void close() {
Environment.env = System.getenv();
Environment.properties = System.getProperties();
}
}
}