mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-27 07:55:25 +00:00
Fix #56 Require Java 8+ instead of Java 11+ at runtime
This commit is contained in:
84
common/pom.xml
Normal file
84
common/pom.xml
Normal file
@@ -0,0 +1,84 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.jboss.fuse.mvnd</groupId>
|
||||
<artifactId>mvnd</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>mvnd-common</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<name>Maven Daemon - Common</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jline</groupId>
|
||||
<artifactId>jline-terminal</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jline</groupId>
|
||||
<artifactId>jline-terminal-jansi</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.takari.maven.plugins</groupId>
|
||||
<artifactId>takari-lifecycle-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>sisu-index</goal>
|
||||
</goals>
|
||||
<phase>process-classes</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
309
common/src/main/java/org/jboss/fuse/mvnd/common/DaemonConnection.java
Executable file
309
common/src/main/java/org/jboss/fuse/mvnd/common/DaemonConnection.java
Executable file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
137
common/src/main/java/org/jboss/fuse/mvnd/common/DaemonInfo.java
Normal file
137
common/src/main/java/org/jboss/fuse/mvnd/common/DaemonInfo.java
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jboss.fuse.mvnd.common.DaemonState.Busy;
|
||||
import static org.jboss.fuse.mvnd.common.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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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.common.DaemonState.Canceled;
|
||||
import static org.jboss.fuse.mvnd.common.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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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
|
||||
+ "}";
|
||||
}
|
||||
|
||||
}
|
293
common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java
Normal file
293
common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
public EnvValue or(ValueSource source) {
|
||||
return new EnvValue(this, envKey, source);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
116
common/src/main/java/org/jboss/fuse/mvnd/common/Layout.java
Normal file
116
common/src/main/java/org/jboss/fuse/mvnd/common/Layout.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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.findBasicMavenHome()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orFail()
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize(),
|
||||
pwd,
|
||||
Environment.findMultiModuleProjectDirectory(pwd));
|
||||
}
|
||||
return ENV_INSTANCE;
|
||||
}
|
||||
|
||||
public 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 + "]";
|
||||
}
|
||||
|
||||
}
|
371
common/src/main/java/org/jboss/fuse/mvnd/common/Message.java
Normal file
371
common/src/main/java/org/jboss/fuse/mvnd/common/Message.java
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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;
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
# 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}
|
@@ -0,0 +1,5 @@
|
||||
# An absolute path to your Maven Daemon installation
|
||||
mvnd.home = %s
|
||||
|
||||
# java.home is optional if you have JAVA_HOME environment variable set
|
||||
%s
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user