[
https://issues.apache.org/jira/browse/MRESOLVER-499?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17819812#comment-17819812
]
ASF GitHub Bot commented on MRESOLVER-499:
------------------------------------------
michael-o commented on code in PR #435:
URL: https://github.com/apache/maven-resolver/pull/435#discussion_r1499907387
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/SocketFamily.java:
##########
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.nio.channels.ServerSocketChannel;
+
+/**
+ * Socket factory.
+ *
+ * @since 2.0.0
+ */
+public enum SocketFamily {
+ inet;
+
+ public ServerSocketChannel openServerSocket() throws IOException {
+ switch (this) {
+ case inet:
+ return ServerSocketChannel.open().bind(new
InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public static SocketAddress fromString(String str) {
+ if (str.startsWith("inet:")) {
Review Comment:
Why this complexity?
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java:
##########
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.Channels;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Implementation of the server side.
+ * The server instance is bound to a given maven repository.
+ *
+ * @since 2.0.0
+ */
+public class IpcServer {
+ /**
+ * Should the IPC server not fork? (i.e. for testing purposes)
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_NO_FORK}
+ */
+ public static final String SYSTEM_PROP_NO_FORK = "aether.named.ipc.nofork";
+
+ public static final boolean DEFAULT_NO_FORK = false;
+
+ /**
+ * IPC idle timeout in seconds. If there is no IPC request during idle
time, it will stop.
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Integer}
+ * @configurationDefaultValue {@link #DEFAULT_IDLE_TIMEOUT}
+ */
+ public static final String SYSTEM_PROP_IDLE_TIMEOUT =
"aether.named.ipc.idleTimeout";
+
+ public static final int DEFAULT_IDLE_TIMEOUT = 60;
+
+ /**
+ * IPC socket family to use.
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.String}
+ * @configurationDefaultValue {@link #DEFAULT_FAMILY}
+ */
+ public static final String SYSTEM_PROP_FAMILY = "aether.named.ipc.family";
+
+ public static final String DEFAULT_FAMILY = "inet";
+
+ /**
+ * Should the IPC server not use native executable?
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_NO_NATIVE}
+ */
+ public static final String SYSTEM_PROP_NO_NATIVE =
"aether.named.ipc.nonative";
+
+ public static final boolean DEFAULT_NO_NATIVE = false;
+
+ /**
+ * Should the IPC server log debug messages? (i.e. for testing purposes)
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_DEBUG}
+ */
+ public static final String SYSTEM_PROP_DEBUG = "aether.named.ipc.debug";
+
+ public static final boolean DEFAULT_DEBUG = false;
+
+ private final ServerSocketChannel serverSocket;
+ private final Map<SocketChannel, Thread> clients = new HashMap<>();
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Map<String, Lock> locks = new ConcurrentHashMap<>();
+ private final Map<String, Context> contexts = new ConcurrentHashMap<>();
+ private static final boolean DEBUG =
+ Boolean.parseBoolean(System.getProperty(SYSTEM_PROP_DEBUG,
Boolean.toString(DEFAULT_DEBUG)));
+ private final long idleTimeout;
+ private volatile long lastUsed;
+ private volatile boolean closing;
+
+ public IpcServer(SocketFamily family) throws IOException {
+ serverSocket = family.openServerSocket();
+ long timeout = TimeUnit.SECONDS.toNanos(DEFAULT_IDLE_TIMEOUT);
+ String str = System.getProperty(SYSTEM_PROP_IDLE_TIMEOUT);
+ if (str != null) {
+ try {
+ TimeUnit unit = TimeUnit.SECONDS;
+ if (str.endsWith("ms")) {
+ unit = TimeUnit.MILLISECONDS;
+ str = str.substring(0, str.length() - 2);
+ }
+ long dur = Long.parseLong(str);
+ timeout = unit.toNanos(dur);
+ } catch (NumberFormatException e) {
+ error("Property " + SYSTEM_PROP_IDLE_TIMEOUT + " specified
with invalid value: " + str, e);
+ }
+ }
+ idleTimeout = timeout;
+ }
+
+ public static void main(String[] args) throws Exception {
+ // When spawning a new process, the child process is create within
+ // the same process group. This means that a few signals are sent
+ // to the whole group. This is the case for SIGINT (Ctrl-C) and
+ // SIGTSTP (Ctrl-Z) which are both sent to all the processed in the
+ // group when initiated from the controlling terminal.
+ // This is only a problem when the client creates the daemon, but
+ // without ignoring those signals, a client being interrupted will
+ // also interrupt and kill the daemon.
+ try {
+ sun.misc.Signal.handle(new sun.misc.Signal("INT"),
sun.misc.SignalHandler.SIG_IGN);
+ if (IpcClient.IS_WINDOWS) {
+ sun.misc.Signal.handle(new sun.misc.Signal("TSTP"),
sun.misc.SignalHandler.SIG_IGN);
+ }
+ } catch (Throwable t) {
+ error("Unable to ignore INT and TSTP signals", t);
+ }
+
+ String family = args[0];
+ String tmpAddress = args[1];
+ String rand = args[2];
+
+ runServer(SocketFamily.valueOf(family), tmpAddress, rand);
+ }
+
+ static IpcServer runServer(SocketFamily family, String tmpAddress, String
rand) throws IOException {
+ IpcServer server = new IpcServer(family);
+ run(server::run, false); // this is one-off
+ String address = SocketFamily.toString(server.getLocalAddress());
+ SocketAddress socketAddress = SocketFamily.fromString(tmpAddress);
+ try (SocketChannel socket = SocketChannel.open(socketAddress)) {
+ try (DataOutputStream dos = new
DataOutputStream(Channels.newOutputStream(socket))) {
+ dos.writeUTF(rand);
+ dos.writeUTF(address);
+ dos.flush();
+ }
+ }
+
+ return server;
+ }
+
+ private static void debug(String msg, Object... args) {
+ if (DEBUG) {
+ System.out.printf("[ipc] [debug] " + msg + "\n", args);
+ }
+ }
+
+ private static void info(String msg, Object... args) {
+ System.out.printf("[ipc] [info] " + msg + "\n", args);
Review Comment:
`%n`
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcClient.java:
##########
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+import java.io.*;
+import java.net.SocketAddress;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.Channels;
+import java.nio.channels.FileLock;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_ACQUIRE;
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_CLOSE;
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_CONTEXT;
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_STOP;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_ACQUIRE;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_CLOSE;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_CONTEXT;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_STOP;
+
+/**
+ * Client side implementation.
+ * The client instance is bound to a given maven repository.
+ *
+ * @since 2.0.0
+ */
+public class IpcClient {
+
+ static final boolean IS_WINDOWS =
+
System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win");
+
+ volatile boolean initialized;
+ Path lockPath;
+ Path logPath;
+ Path syncPath;
+ SocketChannel socket;
+ DataOutputStream output;
+ DataInputStream input;
+ Thread receiver;
+ AtomicInteger requestId = new AtomicInteger();
+ Map<Integer, CompletableFuture<List<String>>> responses = new
ConcurrentHashMap<>();
+
+ IpcClient(Path lockPath, Path logPath, Path syncPath) {
+ this.lockPath = lockPath;
+ this.logPath = logPath;
+ this.syncPath = syncPath;
+ }
+
+ void ensureInitialized() throws IOException {
+ if (!initialized) {
+ synchronized (this) {
+ if (!initialized) {
+ socket = createClient();
+ ByteChannel wrapper = new ByteChannelWrapper(socket);
+ input = new
DataInputStream(Channels.newInputStream(wrapper));
+ output = new
DataOutputStream(Channels.newOutputStream(wrapper));
+ receiver = new Thread(this::receive);
+ receiver.setDaemon(true);
+ receiver.start();
+ initialized = true;
+ }
+ }
+ }
+ }
+
+ SocketChannel createClient() throws IOException {
+ String familyProp = System.getProperty(IpcServer.SYSTEM_PROP_FAMILY,
IpcServer.DEFAULT_FAMILY);
+ SocketFamily family = familyProp != null ?
SocketFamily.valueOf(familyProp) : SocketFamily.inet;
Review Comment:
How can this `null` if you have a default value?
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/SocketFamily.java:
##########
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.nio.channels.ServerSocketChannel;
+
+/**
+ * Socket factory.
+ *
+ * @since 2.0.0
+ */
+public enum SocketFamily {
+ inet;
Review Comment:
Should be uppercase. Akin to `AF_INET`.
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java:
##########
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.Channels;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Implementation of the server side.
+ * The server instance is bound to a given maven repository.
+ *
+ * @since 2.0.0
+ */
+public class IpcServer {
+ /**
+ * Should the IPC server not fork? (i.e. for testing purposes)
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_NO_FORK}
+ */
+ public static final String SYSTEM_PROP_NO_FORK = "aether.named.ipc.nofork";
+
+ public static final boolean DEFAULT_NO_FORK = false;
+
+ /**
+ * IPC idle timeout in seconds. If there is no IPC request during idle
time, it will stop.
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Integer}
+ * @configurationDefaultValue {@link #DEFAULT_IDLE_TIMEOUT}
+ */
+ public static final String SYSTEM_PROP_IDLE_TIMEOUT =
"aether.named.ipc.idleTimeout";
+
+ public static final int DEFAULT_IDLE_TIMEOUT = 60;
+
+ /**
+ * IPC socket family to use.
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.String}
+ * @configurationDefaultValue {@link #DEFAULT_FAMILY}
+ */
+ public static final String SYSTEM_PROP_FAMILY = "aether.named.ipc.family";
+
+ public static final String DEFAULT_FAMILY = "inet";
+
+ /**
+ * Should the IPC server not use native executable?
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_NO_NATIVE}
+ */
+ public static final String SYSTEM_PROP_NO_NATIVE =
"aether.named.ipc.nonative";
+
+ public static final boolean DEFAULT_NO_NATIVE = false;
+
+ /**
+ * Should the IPC server log debug messages? (i.e. for testing purposes)
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_DEBUG}
+ */
+ public static final String SYSTEM_PROP_DEBUG = "aether.named.ipc.debug";
+
+ public static final boolean DEFAULT_DEBUG = false;
+
+ private final ServerSocketChannel serverSocket;
+ private final Map<SocketChannel, Thread> clients = new HashMap<>();
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Map<String, Lock> locks = new ConcurrentHashMap<>();
+ private final Map<String, Context> contexts = new ConcurrentHashMap<>();
+ private static final boolean DEBUG =
+ Boolean.parseBoolean(System.getProperty(SYSTEM_PROP_DEBUG,
Boolean.toString(DEFAULT_DEBUG)));
+ private final long idleTimeout;
+ private volatile long lastUsed;
+ private volatile boolean closing;
+
+ public IpcServer(SocketFamily family) throws IOException {
+ serverSocket = family.openServerSocket();
+ long timeout = TimeUnit.SECONDS.toNanos(DEFAULT_IDLE_TIMEOUT);
+ String str = System.getProperty(SYSTEM_PROP_IDLE_TIMEOUT);
+ if (str != null) {
+ try {
+ TimeUnit unit = TimeUnit.SECONDS;
+ if (str.endsWith("ms")) {
+ unit = TimeUnit.MILLISECONDS;
+ str = str.substring(0, str.length() - 2);
+ }
+ long dur = Long.parseLong(str);
+ timeout = unit.toNanos(dur);
+ } catch (NumberFormatException e) {
+ error("Property " + SYSTEM_PROP_IDLE_TIMEOUT + " specified
with invalid value: " + str, e);
+ }
+ }
+ idleTimeout = timeout;
+ }
+
+ public static void main(String[] args) throws Exception {
+ // When spawning a new process, the child process is create within
+ // the same process group. This means that a few signals are sent
+ // to the whole group. This is the case for SIGINT (Ctrl-C) and
+ // SIGTSTP (Ctrl-Z) which are both sent to all the processed in the
+ // group when initiated from the controlling terminal.
+ // This is only a problem when the client creates the daemon, but
+ // without ignoring those signals, a client being interrupted will
+ // also interrupt and kill the daemon.
+ try {
+ sun.misc.Signal.handle(new sun.misc.Signal("INT"),
sun.misc.SignalHandler.SIG_IGN);
+ if (IpcClient.IS_WINDOWS) {
+ sun.misc.Signal.handle(new sun.misc.Signal("TSTP"),
sun.misc.SignalHandler.SIG_IGN);
Review Comment:
I don't understand this because Windows does not use signals at all.
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcClient.java:
##########
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+import java.io.*;
+import java.net.SocketAddress;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.Channels;
+import java.nio.channels.FileLock;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_ACQUIRE;
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_CLOSE;
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_CONTEXT;
+import static org.eclipse.aether.named.ipc.IpcMessages.REQUEST_STOP;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_ACQUIRE;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_CLOSE;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_CONTEXT;
+import static org.eclipse.aether.named.ipc.IpcMessages.RESPONSE_STOP;
+
+/**
+ * Client side implementation.
+ * The client instance is bound to a given maven repository.
+ *
+ * @since 2.0.0
+ */
+public class IpcClient {
+
+ static final boolean IS_WINDOWS =
+
System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win");
+
+ volatile boolean initialized;
+ Path lockPath;
+ Path logPath;
+ Path syncPath;
+ SocketChannel socket;
+ DataOutputStream output;
+ DataInputStream input;
+ Thread receiver;
+ AtomicInteger requestId = new AtomicInteger();
+ Map<Integer, CompletableFuture<List<String>>> responses = new
ConcurrentHashMap<>();
+
+ IpcClient(Path lockPath, Path logPath, Path syncPath) {
+ this.lockPath = lockPath;
+ this.logPath = logPath;
+ this.syncPath = syncPath;
+ }
+
+ void ensureInitialized() throws IOException {
+ if (!initialized) {
+ synchronized (this) {
+ if (!initialized) {
+ socket = createClient();
+ ByteChannel wrapper = new ByteChannelWrapper(socket);
+ input = new
DataInputStream(Channels.newInputStream(wrapper));
+ output = new
DataOutputStream(Channels.newOutputStream(wrapper));
+ receiver = new Thread(this::receive);
+ receiver.setDaemon(true);
+ receiver.start();
+ initialized = true;
+ }
+ }
+ }
+ }
+
+ SocketChannel createClient() throws IOException {
+ String familyProp = System.getProperty(IpcServer.SYSTEM_PROP_FAMILY,
IpcServer.DEFAULT_FAMILY);
+ SocketFamily family = familyProp != null ?
SocketFamily.valueOf(familyProp) : SocketFamily.inet;
+
+ Path lockPath = this.lockPath.toAbsolutePath().normalize();
+ Path lockFile =
+ lockPath.resolve(".maven-resolver-ipc-lock-" +
family.name().toLowerCase());
Review Comment:
`Locale.ROOT`
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcMessages.java:
##########
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+/**
+ * Constants used for the inter-process communication protocol.
+ *
+ * @since 2.0.0
+ */
+public class IpcMessages {
+
+ public static final String REQUEST_CONTEXT = "request-context";
+ public static final String REQUEST_ACQUIRE = "request-acquire";
+ public static final String REQUEST_CLOSE = "request-close";
+ public static final String REQUEST_STOP = "request-stop";
+ public static final String RESPONSE_CONTEXT = "response-context";
+ public static final String RESPONSE_ACQUIRE = "response-acquire";
+ public static final String RESPONSE_CLOSE = "response-close";
+ public static final String RESPONSE_STOP = "response-stop";
+}
Review Comment:
I wonder wether an enum would be better?!
##########
maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java:
##########
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.named.ipc;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.Channels;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Implementation of the server side.
+ * The server instance is bound to a given maven repository.
+ *
+ * @since 2.0.0
+ */
+public class IpcServer {
+ /**
+ * Should the IPC server not fork? (i.e. for testing purposes)
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_NO_FORK}
+ */
+ public static final String SYSTEM_PROP_NO_FORK = "aether.named.ipc.nofork";
+
+ public static final boolean DEFAULT_NO_FORK = false;
+
+ /**
+ * IPC idle timeout in seconds. If there is no IPC request during idle
time, it will stop.
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Integer}
+ * @configurationDefaultValue {@link #DEFAULT_IDLE_TIMEOUT}
+ */
+ public static final String SYSTEM_PROP_IDLE_TIMEOUT =
"aether.named.ipc.idleTimeout";
+
+ public static final int DEFAULT_IDLE_TIMEOUT = 60;
+
+ /**
+ * IPC socket family to use.
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.String}
+ * @configurationDefaultValue {@link #DEFAULT_FAMILY}
+ */
+ public static final String SYSTEM_PROP_FAMILY = "aether.named.ipc.family";
+
+ public static final String DEFAULT_FAMILY = "inet";
+
+ /**
+ * Should the IPC server not use native executable?
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_NO_NATIVE}
+ */
+ public static final String SYSTEM_PROP_NO_NATIVE =
"aether.named.ipc.nonative";
+
+ public static final boolean DEFAULT_NO_NATIVE = false;
+
+ /**
+ * Should the IPC server log debug messages? (i.e. for testing purposes)
+ *
+ * @configurationSource {@link System#getProperty(String, String)}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationDefaultValue {@link #DEFAULT_DEBUG}
+ */
+ public static final String SYSTEM_PROP_DEBUG = "aether.named.ipc.debug";
+
+ public static final boolean DEFAULT_DEBUG = false;
+
+ private final ServerSocketChannel serverSocket;
+ private final Map<SocketChannel, Thread> clients = new HashMap<>();
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Map<String, Lock> locks = new ConcurrentHashMap<>();
+ private final Map<String, Context> contexts = new ConcurrentHashMap<>();
+ private static final boolean DEBUG =
+ Boolean.parseBoolean(System.getProperty(SYSTEM_PROP_DEBUG,
Boolean.toString(DEFAULT_DEBUG)));
+ private final long idleTimeout;
+ private volatile long lastUsed;
+ private volatile boolean closing;
+
+ public IpcServer(SocketFamily family) throws IOException {
+ serverSocket = family.openServerSocket();
+ long timeout = TimeUnit.SECONDS.toNanos(DEFAULT_IDLE_TIMEOUT);
+ String str = System.getProperty(SYSTEM_PROP_IDLE_TIMEOUT);
+ if (str != null) {
+ try {
+ TimeUnit unit = TimeUnit.SECONDS;
+ if (str.endsWith("ms")) {
+ unit = TimeUnit.MILLISECONDS;
+ str = str.substring(0, str.length() - 2);
+ }
+ long dur = Long.parseLong(str);
+ timeout = unit.toNanos(dur);
+ } catch (NumberFormatException e) {
+ error("Property " + SYSTEM_PROP_IDLE_TIMEOUT + " specified
with invalid value: " + str, e);
+ }
+ }
+ idleTimeout = timeout;
+ }
+
+ public static void main(String[] args) throws Exception {
+ // When spawning a new process, the child process is create within
+ // the same process group. This means that a few signals are sent
+ // to the whole group. This is the case for SIGINT (Ctrl-C) and
+ // SIGTSTP (Ctrl-Z) which are both sent to all the processed in the
+ // group when initiated from the controlling terminal.
+ // This is only a problem when the client creates the daemon, but
+ // without ignoring those signals, a client being interrupted will
+ // also interrupt and kill the daemon.
+ try {
+ sun.misc.Signal.handle(new sun.misc.Signal("INT"),
sun.misc.SignalHandler.SIG_IGN);
+ if (IpcClient.IS_WINDOWS) {
+ sun.misc.Signal.handle(new sun.misc.Signal("TSTP"),
sun.misc.SignalHandler.SIG_IGN);
+ }
+ } catch (Throwable t) {
+ error("Unable to ignore INT and TSTP signals", t);
+ }
+
+ String family = args[0];
+ String tmpAddress = args[1];
+ String rand = args[2];
+
+ runServer(SocketFamily.valueOf(family), tmpAddress, rand);
+ }
+
+ static IpcServer runServer(SocketFamily family, String tmpAddress, String
rand) throws IOException {
+ IpcServer server = new IpcServer(family);
+ run(server::run, false); // this is one-off
+ String address = SocketFamily.toString(server.getLocalAddress());
+ SocketAddress socketAddress = SocketFamily.fromString(tmpAddress);
+ try (SocketChannel socket = SocketChannel.open(socketAddress)) {
+ try (DataOutputStream dos = new
DataOutputStream(Channels.newOutputStream(socket))) {
+ dos.writeUTF(rand);
+ dos.writeUTF(address);
+ dos.flush();
+ }
+ }
+
+ return server;
+ }
+
+ private static void debug(String msg, Object... args) {
+ if (DEBUG) {
+ System.out.printf("[ipc] [debug] " + msg + "\n", args);
Review Comment:
`%n`
> IPC Named Locks
> ---------------
>
> Key: MRESOLVER-499
> URL: https://issues.apache.org/jira/browse/MRESOLVER-499
> Project: Maven Resolver
> Issue Type: New Feature
> Components: Resolver
> Reporter: Tamas Cservenak
> Assignee: Tamas Cservenak
> Priority: Major
> Fix For: 2.0.0, 2.0.0-alpha-9
>
>
> Create IPC named locks implementation. Depends on MRESOLVER-421.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)