You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2022/04/11 11:29:47 UTC

[maven-mvnd] branch master updated: Mvnd with file locking (#508)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-mvnd.git


The following commit(s) were added to refs/heads/master by this push:
     new d2365ee  Mvnd with file locking (#508)
d2365ee is described below

commit d2365ee1662fb4a29cad9d6eb695755857af84a7
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Mon Apr 11 13:29:42 2022 +0200

    Mvnd with file locking (#508)
    
    * Update mvnd to include Maven Resolver 1.7
    
    As mvnd itself is Java8 lang level, it is completely
    okay to up resolver to 1.7 (that is java8 as well).
    The only reason why Maven 3.8.x CANNOT use resolver
    1.7 is that it is still Java7 level.
    
    * Move missing piece to SPI
    
    * Add global exclusions
    
    * Example of mvnd using file locking (of resolver)
    
    * Drop irrelevant
    
    * Upgrade to released 1.7.3
    
    Co-authored-by: Guillaume Nodet <gn...@gmail.com>
---
 .../org/mvndaemon/mvnd/common/Environment.java     |   6 +-
 .../mvnd/sync/MvndSyncContextFactory.java          | 155 --------
 .../syncontext/DaemonNamedLockFactorySelector.java |  61 +++
 dist/src/main/distro/bin/mvnd-bash-completion.bash |   2 +-
 dist/src/main/provisio/maven-distro.xml            |   7 -
 pom.xml                                            |   3 +-
 sync/pom.xml                                       | 132 -------
 .../java/org/mvndaemon/mvnd/sync/IpcClient.java    | 400 --------------------
 .../java/org/mvndaemon/mvnd/sync/IpcMessages.java  |  30 --
 .../java/org/mvndaemon/mvnd/sync/IpcServer.java    | 421 ---------------------
 .../org/mvndaemon/mvnd/sync/IpcSyncContext.java    |  94 -----
 .../mvndaemon/mvnd/sync/IpcSyncContextFactory.java |  67 ----
 .../mvndaemon/mvnd/sync/IpcSyncContextTest.java    | 136 -------
 13 files changed, 64 insertions(+), 1450 deletions(-)

diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java
index f7119c0..90c9d77 100644
--- a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java
+++ b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java
@@ -266,11 +266,7 @@ public enum Environment {
      * This pattern will be evaluated against the full path of the dependencies, so it is usually desirable to
      * start with <code>'glob:**&#47;'</code> to support any location of the local repository.
      */
-    MVND_PLUGIN_REALM_EVICT_PATTERN("mvnd.pluginRealmEvictPattern", null, "", OptionType.STRING, Flags.OPTIONAL),
-    /**
-     * The SyncContextFactory to use (can be either 'noop' or 'ipc' for a server-wide factory).
-     */
-    MVND_SYNC_CONTEXT_FACTORY("mvnd.syncContextFactory", null, "local", OptionType.BOOLEAN, Flags.OPTIONAL);
+    MVND_PLUGIN_REALM_EVICT_PATTERN("mvnd.pluginRealmEvictPattern", null, "", OptionType.STRING, Flags.OPTIONAL);
 
     static Properties properties;
 
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/sync/MvndSyncContextFactory.java b/daemon/src/main/java/org/mvndaemon/mvnd/sync/MvndSyncContextFactory.java
deleted file mode 100644
index 4d7f4c8..0000000
--- a/daemon/src/main/java/org/mvndaemon/mvnd/sync/MvndSyncContextFactory.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.mvndaemon.mvnd.sync;
-
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.stream.Stream;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.SyncContext;
-import org.eclipse.aether.artifact.Artifact;
-import org.eclipse.aether.metadata.Metadata;
-import org.eclipse.aether.spi.synccontext.SyncContextFactory;
-import org.eclipse.sisu.Priority;
-import org.mvndaemon.mvnd.common.Environment;
-
-@Singleton
-@Named
-@Priority(20)
-public class MvndSyncContextFactory implements SyncContextFactory {
-
-    public static final String FACTORY_NOOP = "noop";
-    public static final String FACTORY_IPC = "ipc";
-    public static final String FACTORY_LOCAL = "local";
-
-    public static final String IPC_SYNC_CONTEXT_FACTORY = "org.mvndaemon.mvnd.sync.IpcSyncContextFactory";
-
-    private final Map<String, SyncContextFactory> factories;
-
-    @SuppressWarnings("unchecked")
-    public MvndSyncContextFactory() {
-        try {
-            Map<String, SyncContextFactory> map = new HashMap<>();
-            map.put(FACTORY_LOCAL, new LocalSyncContextFactory());
-            map.put(FACTORY_NOOP, new NoopSyncContextFactory());
-            Class<? extends SyncContextFactory> factoryClass = (Class<? extends SyncContextFactory>) getClass().getClassLoader()
-                    .loadClass(IPC_SYNC_CONTEXT_FACTORY);
-            map.put(FACTORY_IPC, factoryClass.getDeclaredConstructor().newInstance());
-            factories = Collections.unmodifiableMap(map);
-        } catch (Exception e) {
-            throw new RuntimeException("Unable to create IpcSyncContextFactory instance", e);
-        }
-    }
-
-    @Override
-    public SyncContext newInstance(RepositorySystemSession repositorySystemSession, boolean shared) {
-        String name = Environment.MVND_SYNC_CONTEXT_FACTORY.asOptional()
-                .orElseGet(Environment.MVND_SYNC_CONTEXT_FACTORY::getDefault);
-        SyncContextFactory factory = factories.get(name);
-        if (factory == null) {
-            throw new RuntimeException("Unable to find SyncContextFactory named '" + name + "'");
-        }
-        return factory.newInstance(repositorySystemSession, shared);
-    }
-
-    private static class NoopSyncContextFactory implements SyncContextFactory {
-        @Override
-        public SyncContext newInstance(RepositorySystemSession repositorySystemSession, boolean shared) {
-            return new SyncContext() {
-                @Override
-                public void acquire(Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas) {
-                }
-
-                @Override
-                public void close() {
-                }
-            };
-        }
-    }
-
-    private static class LocalSyncContextFactory implements SyncContextFactory {
-        final Map<String, Lock> locks = new ConcurrentHashMap<>();
-
-        @Override
-        public SyncContext newInstance(RepositorySystemSession session, boolean shared) {
-            return new LocalSyncContext();
-        }
-
-        private class LocalSyncContext implements SyncContext {
-            private final Deque<String> locked = new ArrayDeque<>();
-
-            @Override
-            public void acquire(Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas) {
-                stream(artifacts).map(this::getKey).sorted().forEach(this::acquire);
-                stream(metadatas).map(this::getKey).sorted().forEach(this::acquire);
-            }
-
-            private void acquire(String key) {
-                try {
-                    getLock(key).lock();
-                    locked.add(key);
-                } catch (Exception e) {
-                    close();
-                    throw new IllegalStateException("Could not acquire lock for '" + key + "'", e);
-                }
-            }
-
-            @Override
-            public void close() {
-                String key;
-                while ((key = locked.poll()) != null) {
-                    getLock(key).unlock();
-                }
-            }
-
-            private Lock getLock(String key) {
-                return locks.computeIfAbsent(key, k -> new ReentrantLock());
-            }
-
-            private <T> Stream<T> stream(Collection<T> col) {
-                return col != null ? col.stream() : Stream.empty();
-            }
-
-            private String getKey(Artifact a) {
-                return "artifact:" + a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getBaseVersion();
-            }
-
-            private String getKey(Metadata m) {
-                StringBuilder key = new StringBuilder("metadata:");
-                if (!m.getGroupId().isEmpty()) {
-                    key.append(m.getGroupId());
-                    if (!m.getArtifactId().isEmpty()) {
-                        key.append(':').append(m.getArtifactId());
-                        if (!m.getVersion().isEmpty()) {
-                            key.append(':').append(m.getVersion());
-                        }
-                    }
-                }
-                return key.toString();
-            }
-        }
-    }
-
-}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/syncontext/DaemonNamedLockFactorySelector.java b/daemon/src/main/java/org/mvndaemon/mvnd/syncontext/DaemonNamedLockFactorySelector.java
new file mode 100644
index 0000000..8a09da4
--- /dev/null
+++ b/daemon/src/main/java/org/mvndaemon/mvnd/syncontext/DaemonNamedLockFactorySelector.java
@@ -0,0 +1,61 @@
+/*
+ * 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.mvndaemon.mvnd.syncontext;
+
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+/*
+ * 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.
+ */
+import org.eclipse.aether.internal.impl.synccontext.named.FileGAVNameMapper;
+import org.eclipse.aether.internal.impl.synccontext.named.NameMapper;
+import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelectorSupport;
+import org.eclipse.aether.named.NamedLockFactory;
+import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
+import org.eclipse.sisu.Priority;
+
+/**
+ * Mvnd selector implementation: it differs from
+ * {@link org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector} only by default values.
+ */
+@Singleton
+@Named
+@Priority(10)
+public final class DaemonNamedLockFactorySelector
+        extends NamedLockFactorySelectorSupport {
+    @Inject
+    public DaemonNamedLockFactorySelector(final Map<String, NamedLockFactory> factories,
+            final Map<String, NameMapper> nameMappers) {
+        super(factories, FileLockNamedLockFactory.NAME, nameMappers, FileGAVNameMapper.NAME);
+    }
+}
diff --git a/dist/src/main/distro/bin/mvnd-bash-completion.bash b/dist/src/main/distro/bin/mvnd-bash-completion.bash
index 5b7b6ad..65bed44 100644
--- a/dist/src/main/distro/bin/mvnd-bash-completion.bash
+++ b/dist/src/main/distro/bin/mvnd-bash-completion.bash
@@ -147,7 +147,7 @@ _mvnd()
 
     local mvnd_opts="-1"
     local mvnd_long_opts="--color|--completion|--purge|--serial|--status|--stop"
-    local mvnd_properties="-Djava.home|-Djdk.java.options|-Dmaven.multiModuleProjectDirectory|-Dmaven.repo.local|-Dmaven.settings|-Dmvnd.buildTime|-Dmvnd.builder|-Dmvnd.daemonStorage|-Dmvnd.debug|-Dmvnd.duplicateDaemonGracePeriod|-Dmvnd.enableAssertions|-Dmvnd.expirationCheckDelay|-Dmvnd.home|-Dmvnd.idleTimeout|-Dmvnd.jvmArgs|-Dmvnd.keepAlive|-Dmvnd.logPurgePeriod|-Dmvnd.logback|-Dmvnd.maxHeapSize|-Dmvnd.maxLostKeepAlive|-Dmvnd.minHeapSize|-Dmvnd.minThreads|-Dmvnd.noBuffering|-Dmvnd.noDa [...]
+    local mvnd_properties="-Djava.home|-Djdk.java.options|-Dmaven.multiModuleProjectDirectory|-Dmaven.repo.local|-Dmaven.settings|-Dmvnd.buildTime|-Dmvnd.builder|-Dmvnd.daemonStorage|-Dmvnd.debug|-Dmvnd.duplicateDaemonGracePeriod|-Dmvnd.enableAssertions|-Dmvnd.expirationCheckDelay|-Dmvnd.home|-Dmvnd.idleTimeout|-Dmvnd.jvmArgs|-Dmvnd.keepAlive|-Dmvnd.logPurgePeriod|-Dmvnd.logback|-Dmvnd.maxHeapSize|-Dmvnd.maxLostKeepAlive|-Dmvnd.minHeapSize|-Dmvnd.minThreads|-Dmvnd.noBuffering|-Dmvnd.noDa [...]
     local opts="-am|-amd|-B|-C|-c|-cpu|-D|-e|-emp|-ep|-f|-fae|-ff|-fn|-gs|-h|-l|-N|-npr|-npu|-nsu|-o|-P|-pl|-q|-rf|-s|-T|-t|-U|-up|-V|-v|-X|${mvnd_opts}"
     local long_opts="--also-make|--also-make-dependents|--batch-mode|--strict-checksums|--lax-checksums|--check-plugin-updates|--define|--errors|--encrypt-master-password|--encrypt-password|--file|--fail-at-end|--fail-fast|--fail-never|--global-settings|--help|--log-file|--non-recursive|--no-plugin-registry|--no-plugin-updates|--no-snapshot-updates|--offline|--activate-profiles|--projects|--quiet|--resume-from|--settings|--threads|--toolchains|--update-snapshots|--update-plugins|--show-v [...]
 
diff --git a/dist/src/main/provisio/maven-distro.xml b/dist/src/main/provisio/maven-distro.xml
index 31c1e68..0b87833 100644
--- a/dist/src/main/provisio/maven-distro.xml
+++ b/dist/src/main/provisio/maven-distro.xml
@@ -74,9 +74,6 @@
         <artifact id="org.mvndaemon.mvnd:mvnd-client:${project.version}">
             <exclusion id="*:*"/>
         </artifact>
-        <artifact id="org.mvndaemon.mvnd:mvnd-synccontext:${project.version}">
-            <exclusion id="*:*"/>
-        </artifact>
         <artifact id="org.mvndaemon.mvnd:mvnd-agent:${project.version}"/>
         <artifact id="org.mvndaemon.mvnd:mvnd-helper-agent:${project.version}"/>
     </artifactSet>
@@ -94,10 +91,6 @@
             <include>mvnd</include>
             <include>mvnd.exe</include>
         </directory>
-        <directory path="${basedir}/../sync/target">
-            <include>mvnd-sync</include>
-            <include>mvnd-sync.exe</include>
-        </directory>
     </fileSet>
 
     <archive name="mvnd-${project.version}-${os.detected.name}-${os.detected.arch}.zip" executable="**/bin/mvnd"/>
diff --git a/pom.xml b/pom.xml
index 8080a62..16b2253 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,7 +57,7 @@
         <junit.jupiter.version>5.7.2</junit.jupiter.version>
         <logback.version>1.2.10</logback.version>
         <maven.version>3.8.4</maven.version>
-        <maven.resolver.version>1.7.2</maven.resolver.version>
+        <maven.resolver.version>1.7.3</maven.resolver.version>
         <slf4j.version>1.7.35</slf4j.version>
 
         <!-- plugin versions a..z -->
@@ -97,7 +97,6 @@
         <module>common</module>
         <module>client</module>
         <module>daemon</module>
-        <module>sync</module>
         <module>dist</module>
         <module>integration-tests</module>
     </modules>
diff --git a/sync/pom.xml b/sync/pom.xml
deleted file mode 100644
index bf90857..0000000
--- a/sync/pom.xml
+++ /dev/null
@@ -1,132 +0,0 @@
-<!--
-
-    Copyright 2021 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.mvndaemon.mvnd</groupId>
-        <artifactId>mvnd</artifactId>
-        <version>0.7.2-SNAPSHOT</version>
-    </parent>
-
-    <artifactId>mvnd-synccontext</artifactId>
-
-    <packaging>jar</packaging>
-    <name>Maven Daemon - IPC Sync Context</name>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.maven.resolver</groupId>
-            <artifactId>maven-resolver-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.maven.resolver</groupId>
-            <artifactId>maven-resolver-impl</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>javax.annotation</groupId>
-            <artifactId>javax.annotation-api</artifactId>
-            <version>1.3.2</version>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.inject</groupId>
-            <artifactId>jakarta.inject-api</artifactId>
-            <version>${jakarta.inject.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.sisu</groupId>
-            <artifactId>org.eclipse.sisu.inject</artifactId>
-            <version>0.3.4</version>
-        </dependency>
-        <dependency>
-            <groupId>org.mvndaemon.mvnd</groupId>
-            <artifactId>mvnd-common</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-simple</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>
-
-    <profiles>
-        <profile>
-            <id>native</id>
-
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.graalvm.nativeimage</groupId>
-                        <artifactId>native-image-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <goals>
-                                    <goal>native-image</goal>
-                                </goals>
-                                <phase>package</phase>
-                            </execution>
-                        </executions>
-                        <configuration>
-                            <skip>false</skip>
-                            <mainClass>org.mvndaemon.mvnd.sync.IpcServer</mainClass>
-                            <imageName>mvnd-sync</imageName>
-                            <buildArgs>
-                                --no-server
-                                --no-fallback
-                                --allow-incomplete-classpath
-                                -H:-ParseRuntimeOptions
-                                -ea
-                            </buildArgs>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
-</project>
\ No newline at end of file
diff --git a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcClient.java b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcClient.java
deleted file mode 100644
index 031af52..0000000
--- a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcClient.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright 2021 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.mvndaemon.mvnd.sync;
-
-import java.io.Closeable;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.io.PrintWriter;
-import java.io.RandomAccessFile;
-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.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.mvndaemon.mvnd.common.ByteChannelWrapper;
-import org.mvndaemon.mvnd.common.JavaVersion;
-import org.mvndaemon.mvnd.common.Os;
-import org.mvndaemon.mvnd.common.SocketFamily;
-
-import static org.mvndaemon.mvnd.sync.IpcMessages.REQUEST_ACQUIRE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.REQUEST_CLOSE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.REQUEST_CONTEXT;
-import static org.mvndaemon.mvnd.sync.IpcMessages.RESPONSE_ACQUIRE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.RESPONSE_CLOSE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.RESPONSE_CONTEXT;
-import static org.mvndaemon.mvnd.sync.IpcServer.FAMILY_PROP;
-
-/**
- * Client side implementation.
- * The client instance is bound to a given maven repository.
- */
-public class IpcClient {
-
-    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;
-    }
-
-    synchronized void ensureInitialized() throws IOException {
-        if (socket == null) {
-            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();
-        }
-    }
-
-    SocketChannel createClient() throws IOException {
-        String familyProp = System.getProperty(FAMILY_PROP);
-        SocketFamily family = familyProp != null
-                ? SocketFamily.valueOf(familyProp)
-                : JavaVersion.getJavaSpec() >= 16.0f ? SocketFamily.unix : SocketFamily.inet;
-
-        Path lockPath = this.lockPath.toAbsolutePath().normalize();
-        Path lockFile = lockPath.resolve(".maven-resolver-ipc-lock-" + family.name().toLowerCase(Locale.ROOT));
-        if (!Files.isRegularFile(lockFile)) {
-            if (!Files.isDirectory(lockFile.getParent())) {
-                Files.createDirectories(lockFile.getParent());
-            }
-        }
-
-        try (RandomAccessFile raf = new RandomAccessFile(lockFile.toFile(), "rw")) {
-            try (FileLock lock = raf.getChannel().lock()) {
-                String line = raf.readLine();
-                if (line != null) {
-                    try {
-                        SocketAddress address = SocketFamily.fromString(line);
-                        return SocketChannel.open(address);
-                    } catch (IOException e) {
-                        // ignore
-                    }
-                }
-
-                ServerSocketChannel ss = family.openServerSocket();
-                String tmpaddr = SocketFamily.toString(ss.getLocalAddress());
-                String rand = Long.toHexString(new Random().nextLong());
-
-                boolean noNative = Boolean.getBoolean(IpcServer.NO_NATIVE_PROP);
-                boolean win = System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win");
-                if (!noNative) {
-                    String syncCmd = win ? "mvnd-sync.exe" : "mvnd-sync";
-                    noNative = !Files.isExecutable(syncPath.resolve(syncCmd));
-                }
-                Closeable close;
-                Path logFile = logPath.resolve("mvnd-sync-" + rand + ".log");
-                List<String> args = null;
-                if (noNative) {
-                    String noFork = System.getProperty(IpcServer.NO_FORK_PROP);
-                    if (Boolean.parseBoolean(noFork)) {
-                        IpcServer server = IpcServer.runServer(family, tmpaddr, rand);
-                        close = server::close;
-                    } else {
-                        args = new ArrayList<>();
-                        String javaHome = System.getenv("JAVA_HOME");
-                        if (javaHome == null) {
-                            javaHome = System.getProperty("java.home");
-                        }
-                        String javaCmd = win ? "bin\\java.exe" : "bin/java";
-                        String java = Paths.get(javaHome).resolve(javaCmd).toAbsolutePath().toString();
-                        args.add(java);
-                        String classpath = getJarPath(getClass()) + File.pathSeparator + getJarPath(SocketFamily.class);
-                        args.add("-cp");
-                        args.add(classpath);
-                        String timeout = System.getProperty(IpcServer.IDLE_TIMEOUT_PROP);
-                        if (timeout != null) {
-                            args.add("-D" + IpcServer.IDLE_TIMEOUT_PROP + "=" + timeout);
-                        }
-                        args.add(IpcServer.class.getName());
-                        args.add(family.name());
-                        args.add(tmpaddr);
-                        args.add(rand);
-                        ProcessBuilder processBuilder = new ProcessBuilder();
-                        ProcessBuilder.Redirect discard = ProcessBuilder.Redirect.to(new File(win ? "NUL" : "/dev/null"));
-                        discard = ProcessBuilder.Redirect.INHERIT;
-                        Files.createDirectories(logPath);
-                        discard = ProcessBuilder.Redirect.to(logFile.toFile());
-                        Process process = processBuilder
-                                .directory(lockFile.getParent().toFile())
-                                .command(args)
-                                .redirectOutput(discard)
-                                .redirectError(discard)
-                                .start();
-                        close = process::destroyForcibly;
-                    }
-                } else {
-                    args = new ArrayList<>();
-                    String syncCmd = win ? "mvnd-sync.exe" : "mvnd-sync";
-                    args.add(syncPath.resolve(syncCmd).toString());
-                    String timeout = System.getProperty(IpcServer.IDLE_TIMEOUT_PROP);
-                    if (timeout != null) {
-                        args.add("-D" + IpcServer.IDLE_TIMEOUT_PROP + "=" + timeout);
-                    }
-                    args.add(family.name());
-                    args.add(tmpaddr);
-                    args.add(rand);
-                    ProcessBuilder processBuilder = new ProcessBuilder();
-                    ProcessBuilder.Redirect discard = ProcessBuilder.Redirect.to(new File(win ? "NUL" : "/dev/null"));
-                    discard = ProcessBuilder.Redirect.INHERIT;
-                    Files.createDirectories(logPath);
-                    discard = ProcessBuilder.Redirect.to(logFile.toFile());
-                    Process process = processBuilder
-                            .directory(lockFile.getParent().toFile())
-                            .command(args)
-                            .redirectOutput(discard)
-                            .redirectError(discard)
-                            .start();
-                    close = process::destroyForcibly;
-                }
-
-                ExecutorService es = Executors.newSingleThreadExecutor();
-                Future<String[]> future = es.submit(() -> {
-                    SocketChannel s = ss.accept();
-                    DataInputStream dis = new DataInputStream(Channels.newInputStream(s));
-                    String rand2 = dis.readUTF();
-                    String addr2 = dis.readUTF();
-                    return new String[] { rand2, addr2 };
-                });
-                String[] res;
-                try {
-                    res = future.get(5, TimeUnit.SECONDS);
-                } catch (Exception e) {
-                    try (PrintWriter writer = new PrintWriter(new FileWriter(logFile.toFile(), true))) {
-                        writer.println("Arguments:");
-                        args.forEach(writer::println);
-                        writer.println();
-                        writer.println("Exception:");
-                        e.printStackTrace(writer);
-                    }
-                    close.close();
-                    throw e;
-                } finally {
-                    es.shutdownNow();
-                    ss.close();
-                }
-                if (!Objects.equals(rand, res[0])) {
-                    close.close();
-                    throw new IllegalStateException("IpcServer did not respond with the correct random");
-                }
-
-                SocketAddress addr = SocketFamily.fromString(res[1]);
-                SocketChannel socket = SocketChannel.open(addr);
-
-                raf.seek(0);
-                raf.writeBytes(res[1] + "\n");
-                return socket;
-            } catch (Exception e) {
-                throw new RuntimeException("Unable to create and connect to lock server", e);
-            }
-        }
-    }
-
-    private String getJarPath(Class clazz) {
-        String classpath;
-        String className = clazz.getName().replace('.', '/') + ".class";
-        String url = clazz.getClassLoader().getResource(className).toString();
-        if (url.startsWith("jar:")) {
-            url = url.substring("jar:".length(), url.indexOf("!/"));
-            if (url.startsWith("file:")) {
-                classpath = url.substring("file:".length());
-            } else {
-                throw new IllegalStateException();
-            }
-        } else if (url.startsWith("file:")) {
-            classpath = url.substring("file:".length(), url.indexOf(className));
-        } else {
-            throw new IllegalStateException();
-        }
-        if (Os.current() == Os.WINDOWS) {
-            if (classpath.startsWith("/")) {
-                classpath = classpath.substring(1);
-            }
-            classpath = classpath.replace('/', '\\');
-        }
-
-        return classpath;
-    }
-
-    void receive() {
-        try {
-            while (true) {
-                int id = input.readInt();
-                int sz = input.readInt();
-                List<String> s = new ArrayList<>(sz);
-                for (int i = 0; i < sz; i++) {
-                    s.add(input.readUTF());
-                }
-                CompletableFuture<List<String>> f = responses.remove(id);
-                if (f == null || s.isEmpty()) {
-                    throw new IllegalStateException("Protocol error");
-                }
-                f.complete(s);
-            }
-        } catch (Exception e) {
-            close(e);
-        }
-    }
-
-    List<String> send(List<String> request) throws IOException {
-        ensureInitialized();
-        int id = requestId.incrementAndGet();
-        CompletableFuture<List<String>> response = new CompletableFuture<>();
-        responses.put(id, response);
-        synchronized (output) {
-            output.writeInt(id);
-            output.writeInt(request.size());
-            for (String s : request) {
-                output.writeUTF(s);
-            }
-            output.flush();
-        }
-        try {
-            return response.get();
-        } catch (InterruptedException e) {
-            throw (IOException) new InterruptedIOException("Interrupted").initCause(e);
-        } catch (ExecutionException e) {
-            throw new IOException("Execution error", e);
-        }
-    }
-
-    void close() {
-        close(new IOException("Closing"));
-    }
-
-    synchronized void close(Throwable e) {
-        if (socket != null) {
-            try {
-                socket.close();
-            } catch (IOException t) {
-                e.addSuppressed(t);
-            }
-            socket = null;
-            input = null;
-            output = null;
-        }
-        if (receiver != null) {
-            receiver.interrupt();
-            try {
-                receiver.join(1000);
-            } catch (InterruptedException t) {
-                e.addSuppressed(t);
-            }
-        }
-        responses.values().forEach(f -> f.completeExceptionally(e));
-        responses.clear();
-    }
-
-    String newContext(boolean shared) {
-        RuntimeException error = new RuntimeException("Unable to create new sync context");
-        for (int i = 0; i < 2; i++) {
-            try {
-                List<String> response = send(Arrays.asList(
-                        REQUEST_CONTEXT, Boolean.toString(shared)));
-                if (response.size() != 2 || !RESPONSE_CONTEXT.equals(response.get(0))) {
-                    throw new IOException("Unexpected response: " + response);
-                }
-                return response.get(1);
-            } catch (Exception e) {
-                close(e);
-                error.addSuppressed(e);
-            }
-        }
-        throw error;
-    }
-
-    void lock(String contextId, Collection<String> keys) {
-        try {
-            List<String> req = new ArrayList<>(keys.size() + 2);
-            req.add(REQUEST_ACQUIRE);
-            req.add(contextId);
-            req.addAll(keys);
-            List<String> response = send(req);
-            if (response.size() != 1 || !RESPONSE_ACQUIRE.equals(response.get(0))) {
-                throw new IOException("Unexpected response: " + response);
-            }
-        } catch (Exception e) {
-            close(e);
-            throw new RuntimeException("Unable to perform lock (contextId = " + contextId + ")", e);
-        }
-    }
-
-    void unlock(String contextId) {
-        try {
-            List<String> response = send(Arrays.asList(REQUEST_CLOSE, contextId));
-            if (response.size() != 1 || !RESPONSE_CLOSE.equals(response.get(0))) {
-                throw new IOException("Unexpected response: " + response);
-            }
-        } catch (Exception e) {
-            close(e);
-            throw new RuntimeException("Unable to unlock (contextId = " + contextId + ")", e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "IpcClient{"
-                + "lockPath=" + lockPath + ","
-                + "syncServerPath=" + syncPath + ","
-                + "address='" + getAddress() + "'}";
-    }
-
-    private String getAddress() {
-        try {
-            return SocketFamily.toString(socket.getLocalAddress());
-        } catch (IOException e) {
-            return "[not bound]";
-        }
-    }
-
-}
diff --git a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcMessages.java b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcMessages.java
deleted file mode 100644
index 76274f7..0000000
--- a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcMessages.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2021 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.mvndaemon.mvnd.sync;
-
-/**
- * Constants used for the inter-process communication protocol.
- */
-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 RESPONSE_CONTEXT = "response-context";
-    public static final String RESPONSE_ACQUIRE = "response-acquire";
-    public static final String RESPONSE_CLOSE = "response-close";
-
-}
diff --git a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcServer.java b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcServer.java
deleted file mode 100644
index 5e3c642..0000000
--- a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcServer.java
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright 2021 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.mvndaemon.mvnd.sync;
-
-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.Locale;
-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;
-import org.mvndaemon.mvnd.common.ByteChannelWrapper;
-import org.mvndaemon.mvnd.common.SocketFamily;
-
-import static org.mvndaemon.mvnd.sync.IpcMessages.REQUEST_ACQUIRE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.REQUEST_CLOSE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.REQUEST_CONTEXT;
-import static org.mvndaemon.mvnd.sync.IpcMessages.RESPONSE_ACQUIRE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.RESPONSE_CLOSE;
-import static org.mvndaemon.mvnd.sync.IpcMessages.RESPONSE_CONTEXT;
-
-/**
- * Implementation of the server side.
- * The server instance is bound to a given maven repository.
- */
-public class IpcServer {
-
-    public static final String NO_FORK_PROP = "ipcsync.nofork";
-
-    public static final String IDLE_TIMEOUT_PROP = "ipcsync.idle.timeout";
-
-    public static final String FAMILY_PROP = "ipcsync.family";
-
-    public static final String NO_NATIVE_PROP = "ipcsync.nonative";
-
-    static final long IDLE_TIMEOUT = TimeUnit.SECONDS.toNanos(60);
-
-    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 final long idleTimeout;
-    private volatile long lastUsed;
-    private volatile boolean closing;
-
-    public IpcServer(SocketFamily family) throws IOException {
-        serverSocket = family.openServerSocket();
-        long timeout = IDLE_TIMEOUT;
-        String str = System.getProperty(IDLE_TIMEOUT_PROP);
-        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 " + IDLE_TIMEOUT_PROP + " 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 (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win")) {
-                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);
-        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) {
-        //System.out.printf("[ipc] [debug] " + msg + "\n", args);
-    }
-
-    private static void info(String msg, Object... args) {
-        System.out.printf("[ipc] [info] " + msg + "\n", args);
-    }
-
-    private static void error(String msg, Throwable t) {
-        System.out.println("[ipc] [error] " + msg);
-        t.printStackTrace(System.out);
-    }
-
-    private static void run(Runnable runnable) {
-        Thread thread = new Thread(runnable);
-        thread.start();
-    }
-
-    public SocketAddress getLocalAddress() throws IOException {
-        return serverSocket.getLocalAddress();
-    }
-
-    public void run() {
-        try {
-            info("IpcServer started at %s", getLocalAddress().toString());
-            use();
-            run(this::expirationCheck);
-            while (!closing) {
-                SocketChannel socket = this.serverSocket.accept();
-                run(() -> client(socket));
-            }
-        } catch (Throwable t) {
-            if (!closing) {
-                error("Error running sync server loop", t);
-            }
-        }
-    }
-
-    private void client(SocketChannel socket) {
-        int c;
-        synchronized (clients) {
-            clients.put(socket, Thread.currentThread());
-            c = clients.size();
-        }
-        info("New client connected (%d connected)", c);
-        use();
-        Map<String, Context> clientContexts = new ConcurrentHashMap<>();
-        try {
-            ByteChannel wrapper = new ByteChannelWrapper(socket);
-            DataInputStream input = new DataInputStream(Channels.newInputStream(wrapper));
-            DataOutputStream output = new DataOutputStream(Channels.newOutputStream(wrapper));
-            while (!closing) {
-                int requestId = input.readInt();
-                int sz = input.readInt();
-                List<String> request = new ArrayList<>(sz);
-                for (int i = 0; i < sz; i++) {
-                    request.add(input.readUTF());
-                }
-                if (request.isEmpty()) {
-                    throw new IOException("Received invalid request");
-                }
-                use();
-                String contextId;
-                Context context;
-                String command = request.remove(0);
-                switch (command) {
-                case REQUEST_CONTEXT:
-                    if (request.size() != 1) {
-                        throw new IOException("Expected one argument for " + command + " but got " + request);
-                    }
-                    boolean shared = Boolean.parseBoolean(request.remove(0));
-                    context = new Context(shared);
-                    contexts.put(context.id, context);
-                    clientContexts.put(context.id, context);
-                    synchronized (output) {
-                        debug("Created context %s", context.id);
-                        output.writeInt(requestId);
-                        output.writeInt(2);
-                        output.writeUTF(RESPONSE_CONTEXT);
-                        output.writeUTF(context.id);
-                        output.flush();
-                    }
-                    break;
-                case REQUEST_ACQUIRE:
-                    if (request.size() < 1) {
-                        throw new IOException("Expected at least one argument for " + command + " but got " + request);
-                    }
-                    contextId = request.remove(0);
-                    context = contexts.get(contextId);
-                    if (context == null) {
-                        throw new IOException("Unknown context: " + contextId + ". Known contexts = " + contexts.keySet());
-                    }
-                    context.lock(request).thenRun(() -> {
-                        try {
-                            synchronized (output) {
-                                debug("Locking in context %s", context.id);
-                                output.writeInt(requestId);
-                                output.writeInt(1);
-                                output.writeUTF(RESPONSE_ACQUIRE);
-                                output.flush();
-                            }
-                        } catch (IOException e) {
-                            try {
-                                socket.close();
-                            } catch (IOException ioException) {
-                                e.addSuppressed(ioException);
-                            }
-                            error("Error writing lock response", e);
-                        }
-                    });
-                    break;
-                case REQUEST_CLOSE:
-                    if (request.size() != 1) {
-                        throw new IOException("Expected one argument for " + command + " but got " + request);
-                    }
-                    contextId = request.remove(0);
-                    context = contexts.remove(contextId);
-                    clientContexts.remove(contextId);
-                    if (context == null) {
-                        throw new IOException("Unknown context: " + contextId + ". Known contexts = " + contexts.keySet());
-                    }
-                    context.unlock();
-                    synchronized (output) {
-                        debug("Closing context %s", context.id);
-                        output.writeInt(requestId);
-                        output.writeInt(1);
-                        output.writeUTF(RESPONSE_CLOSE);
-                        output.flush();
-                    }
-                    break;
-                default:
-                    throw new IOException("Unknown request: " + request.get(0));
-                }
-            }
-        } catch (Throwable t) {
-            if (!closing) {
-                error("Error processing request", t);
-            }
-        } finally {
-            if (!closing) {
-                info("Client disconnecting...");
-            }
-            clientContexts.values().forEach(context -> {
-                contexts.remove(context.id);
-                context.unlock();
-            });
-            try {
-                socket.close();
-            } catch (IOException ioException) {
-                // ignore
-            }
-            synchronized (clients) {
-                clients.remove(socket);
-                c = clients.size();
-            }
-            if (!closing) {
-                info("%d clients left", c);
-            }
-        }
-    }
-
-    private void use() {
-        lastUsed = System.nanoTime();
-    }
-
-    private void expirationCheck() {
-        while (true) {
-            long current = System.nanoTime();
-            long left = (lastUsed + idleTimeout) - current;
-            if (left < 0) {
-                info("IpcServer expired, closing");
-                close();
-                break;
-            } else {
-                try {
-                    Thread.sleep(TimeUnit.NANOSECONDS.toMillis(left));
-                } catch (InterruptedException e) {
-                    info("IpcServer expiration check interrupted, closing");
-                    close();
-                    break;
-                }
-            }
-        }
-    }
-
-    void close() {
-        closing = true;
-        try {
-            serverSocket.close();
-        } catch (IOException e) {
-            error("Error closing server socket", e);
-        }
-        clients.forEach((s, t) -> {
-            try {
-                s.close();
-            } catch (IOException e) {
-                // ignore
-            }
-            t.interrupt();
-        });
-    }
-
-    static class Waiter {
-        final Context context;
-        final CompletableFuture<Void> future;
-
-        Waiter(Context context, CompletableFuture<Void> future) {
-            this.context = context;
-            this.future = future;
-        }
-    }
-
-    static class Lock {
-
-        final String key;
-
-        List<Context> holders;
-        List<Waiter> waiters;
-
-        Lock(String key) {
-            this.key = key;
-        }
-
-        public synchronized CompletableFuture<Void> lock(Context context) {
-            if (holders == null) {
-                holders = new ArrayList<>();
-            }
-            if (holders.isEmpty() || holders.get(0).shared && context.shared) {
-                holders.add(context);
-                return CompletableFuture.completedFuture(null);
-            }
-            if (waiters == null) {
-                waiters = new ArrayList<>();
-            }
-
-            CompletableFuture<Void> future = new CompletableFuture<>();
-            waiters.add(new Waiter(context, future));
-            return future;
-        }
-
-        public synchronized void unlock(Context context) {
-            if (holders.remove(context)) {
-                while (waiters != null && !waiters.isEmpty()
-                        && (holders.isEmpty() || holders.get(0).shared && waiters.get(0).context.shared)) {
-                    Waiter waiter = waiters.remove(0);
-                    holders.add(waiter.context);
-                    waiter.future.complete(null);
-                }
-            } else if (waiters != null) {
-                for (Iterator<Waiter> it = waiters.iterator(); it.hasNext();) {
-                    Waiter waiter = it.next();
-                    if (waiter.context == context) {
-                        it.remove();
-                        waiter.future.cancel(false);
-                    }
-                }
-            }
-        }
-
-    }
-
-    class Context {
-
-        final String id;
-        final boolean shared;
-        final List<String> locks = new CopyOnWriteArrayList<>();
-
-        Context(boolean shared) {
-            this.id = String.format("%08x", counter.incrementAndGet());
-            this.shared = shared;
-        }
-
-        public CompletableFuture<?> lock(List<String> keys) {
-            locks.addAll(keys);
-            CompletableFuture<?>[] futures = keys.stream()
-                    .map(k -> IpcServer.this.locks.computeIfAbsent(k, Lock::new))
-                    .map(l -> l.lock(this))
-                    .toArray(CompletableFuture[]::new);
-            return CompletableFuture.allOf(futures);
-        }
-
-        public void unlock() {
-            locks.stream()
-                    .map(k -> IpcServer.this.locks.computeIfAbsent(k, Lock::new))
-                    .forEach(l -> l.unlock(this));
-        }
-    }
-
-}
diff --git a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContext.java b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContext.java
deleted file mode 100644
index 9fcb8da..0000000
--- a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContext.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2021 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.mvndaemon.mvnd.sync;
-
-import java.util.Collection;
-import java.util.Objects;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Stream;
-import org.eclipse.aether.SyncContext;
-import org.eclipse.aether.artifact.Artifact;
-import org.eclipse.aether.metadata.Metadata;
-
-/**
- * The SyncContext implementation.
- */
-class IpcSyncContext implements SyncContext {
-
-    final IpcClient client;
-    final boolean shared;
-    final String contextId;
-    final AtomicBoolean closed = new AtomicBoolean();
-
-    IpcSyncContext(IpcClient client, boolean shared) {
-        this.client = client;
-        this.shared = shared;
-        this.contextId = Objects.requireNonNull(client.newContext(shared));
-    }
-
-    @Override
-    public void acquire(Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas) {
-        if (closed.get()) {
-            throw new IllegalStateException("Already closed");
-        }
-        Collection<String> keys = new TreeSet<>();
-        stream(artifacts).map(this::getKey).forEach(keys::add);
-        stream(metadatas).map(this::getKey).forEach(keys::add);
-        if (keys.isEmpty()) {
-            return;
-        }
-        client.lock(contextId, keys);
-    }
-
-    @Override
-    public void close() {
-        if (closed.compareAndSet(false, true)) {
-            client.unlock(contextId);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "IpcSyncContext{"
-                + "client=" + client
-                + ", shared=" + shared
-                + ", contextId='" + contextId + '\''
-                + '}';
-    }
-
-    private <T> Stream<T> stream(Collection<T> col) {
-        return col != null ? col.stream() : Stream.empty();
-    }
-
-    private String getKey(Artifact a) {
-        return "artifact:" + a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getBaseVersion();
-    }
-
-    private String getKey(Metadata m) {
-        StringBuilder key = new StringBuilder("metadata:");
-        if (!m.getGroupId().isEmpty()) {
-            key.append(m.getGroupId());
-            if (!m.getArtifactId().isEmpty()) {
-                key.append(':').append(m.getArtifactId());
-                if (!m.getVersion().isEmpty()) {
-                    key.append(':').append(m.getVersion());
-                }
-            }
-        }
-        return key.toString();
-    }
-}
diff --git a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContextFactory.java b/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContextFactory.java
deleted file mode 100644
index f78e668..0000000
--- a/sync/src/main/java/org/mvndaemon/mvnd/sync/IpcSyncContextFactory.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2021 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.mvndaemon.mvnd.sync;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import javax.annotation.PreDestroy;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.SyncContext;
-import org.eclipse.aether.spi.synccontext.SyncContextFactory;
-import org.eclipse.sisu.Priority;
-import org.mvndaemon.mvnd.common.BuildProperties;
-import org.mvndaemon.mvnd.common.Environment;
-
-/**
- * The SyncContextFactory implementation.
- */
-@Named
-@Priority(10)
-@Singleton
-public class IpcSyncContextFactory implements SyncContextFactory {
-
-    private final Map<Path, IpcClient> clients = new ConcurrentHashMap<>();
-
-    @Override
-    public SyncContext newInstance(RepositorySystemSession session, boolean shared) {
-        Path repository = session.getLocalRepository().getBasedir().toPath();
-        Path logPath = Optional.ofNullable(System.getProperty(Environment.MVND_DAEMON_STORAGE.getProperty()))
-                .map(Paths::get)
-                .orElseGet(() -> Environment.USER_HOME.asPath()
-                        .resolve(".m2/mvnd/registry/" + BuildProperties.getInstance().getVersion()));
-        String mvndHome = Environment.MVND_HOME.asString();
-        Path syncPath = mvndHome != null ? Paths.get(mvndHome).resolve("bin") : null;
-        IpcClient client = clients.computeIfAbsent(repository, r -> new IpcClient(r, logPath, syncPath));
-        return new IpcSyncContext(client, shared);
-    }
-
-    @PreDestroy
-    void close() {
-        clients.values().forEach(IpcClient::close);
-    }
-
-    @Override
-    public String toString() {
-        return "IpcSyncContextFactory{"
-                + "clients=" + clients
-                + '}';
-    }
-}
diff --git a/sync/src/test/java/org/mvndaemon/mvnd/sync/IpcSyncContextTest.java b/sync/src/test/java/org/mvndaemon/mvnd/sync/IpcSyncContextTest.java
deleted file mode 100644
index cb0992c..0000000
--- a/sync/src/test/java/org/mvndaemon/mvnd/sync/IpcSyncContextTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2021 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.mvndaemon.mvnd.sync;
-
-import java.io.File;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.eclipse.aether.SyncContext;
-import org.eclipse.aether.artifact.Artifact;
-import org.eclipse.aether.artifact.DefaultArtifact;
-import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
-import org.eclipse.aether.repository.LocalRepository;
-import org.eclipse.aether.repository.LocalRepositoryManager;
-import org.eclipse.aether.spi.synccontext.SyncContextFactory;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.mvndaemon.mvnd.common.Environment;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class IpcSyncContextTest {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(IpcSyncContextTest.class);
-
-    @BeforeAll
-    static void setup() {
-        Path target = Paths.get(System.getProperty("basedir", "")).resolve("target");
-        System.setProperty(Environment.MVND_DAEMON_STORAGE.getProperty(), target.resolve("mvnd/storage").toString());
-        System.setProperty(Environment.MVND_HOME.getProperty(), target.resolve("mvnd/home").toString());
-        System.setProperty(IpcServer.IDLE_TIMEOUT_PROP, "5");
-        System.setProperty(IpcServer.FAMILY_PROP, "inet");
-        System.setProperty(IpcServer.NO_NATIVE_PROP, "true");
-    }
-
-    @AfterAll
-    static void tearDown() {
-        System.clearProperty(IpcServer.IDLE_TIMEOUT_PROP);
-    }
-
-    @Test
-    public void testContextSimple() throws Exception {
-        SyncContextFactory factory = new IpcSyncContextFactory();
-
-        DefaultRepositorySystemSession session = new DefaultRepositorySystemSession();
-        LocalRepository repository = new LocalRepository(new File("target/mvnd/test-repo"));
-        LocalRepositoryManager localRepositoryManager = new SimpleLocalRepositoryManagerFactory()
-                .newInstance(session, repository);
-        session.setLocalRepositoryManager(localRepositoryManager);
-        Artifact artifact = new DefaultArtifact("myGroup", "myArtifact", "jar", "0.1");
-
-        try (SyncContext context = factory.newInstance(session, false)) {
-            context.acquire(Collections.singleton(artifact), null);
-            Thread.sleep(50);
-        }
-    }
-
-    @Test
-    public void testContext() throws Exception {
-        SyncContextFactory factory = new IpcSyncContextFactory();
-
-        DefaultRepositorySystemSession session = new DefaultRepositorySystemSession();
-        LocalRepository repository = new LocalRepository(new File("target/mvnd/test-repo"));
-        LocalRepositoryManager localRepositoryManager = new SimpleLocalRepositoryManagerFactory()
-                .newInstance(session, repository);
-        session.setLocalRepositoryManager(localRepositoryManager);
-        Artifact artifact = new DefaultArtifact("myGroup", "myArtifact", "jar", "0.1");
-
-        Thread[] threads = new Thread[10];
-        for (int i = 0; i < threads.length; i++) {
-            threads[i] = new Thread(() -> {
-                try (SyncContext context = factory.newInstance(session, false)) {
-                    LOGGER.info("Trying to lock from {}", context);
-                    context.acquire(Collections.singleton(artifact), null);
-                    LOGGER.info("Lock acquired from {}", context);
-                    try {
-                        Thread.sleep(50);
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                    LOGGER.info("Unlock from {}", context);
-                }
-            });
-            threads[i].start();
-        }
-
-        for (Thread thread : threads) {
-            thread.join();
-        }
-    }
-
-    @Test
-    void testTimeoutAndConnect() throws Exception {
-        System.setProperty(IpcServer.IDLE_TIMEOUT_PROP, "50ms");
-        System.setProperty(IpcServer.NO_FORK_PROP, "true");
-        try {
-
-            SyncContextFactory factory = new IpcSyncContextFactory();
-
-            DefaultRepositorySystemSession session = new DefaultRepositorySystemSession();
-            LocalRepository repository = new LocalRepository(new File("target/mvnd/test-repo"));
-            LocalRepositoryManager localRepositoryManager = new SimpleLocalRepositoryManagerFactory()
-                    .newInstance(session, repository);
-            session.setLocalRepositoryManager(localRepositoryManager);
-            Artifact artifact = new DefaultArtifact("myGroup", "myArtifact", "jar", "0.1");
-
-            for (int i = 0; i < 10; i++) {
-                LOGGER.info("[client] Creating sync context");
-                try (SyncContext context = factory.newInstance(session, false)) {
-                    LOGGER.info("[client] Sync context created: {}", context.toString());
-                    context.acquire(Collections.singleton(artifact), null);
-                }
-                LOGGER.info("[client] Sync context closed");
-                Thread.sleep(100);
-            }
-        } finally {
-            System.clearProperty(IpcServer.IDLE_TIMEOUT_PROP);
-            System.clearProperty(IpcServer.NO_FORK_PROP);
-        }
-    }
-}