[ 
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)

Reply via email to