001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.named.ipc; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023import javax.inject.Singleton; 024 025import java.nio.file.Path; 026import java.nio.file.Paths; 027import java.util.Collection; 028import java.util.stream.Collectors; 029 030import org.eclipse.aether.named.NamedLock; 031import org.eclipse.aether.named.NamedLockKey; 032import org.eclipse.aether.named.support.NamedLockFactorySupport; 033import org.eclipse.aether.named.support.NamedLockSupport; 034import org.eclipse.aether.util.StringDigestUtil; 035 036import static java.util.Objects.requireNonNull; 037 038/** 039 * IPC named locks factory. 040 * 041 * @since 2.0.1 042 */ 043@Singleton 044@Named(IpcNamedLockFactory.NAME) 045public class IpcNamedLockFactory extends NamedLockFactorySupport { 046 public static final String NAME = "ipc"; 047 048 protected final IpcClient client; 049 050 @Inject 051 public IpcNamedLockFactory() { 052 this(Paths.get(System.getProperty("user.home")).resolve(".ipc-sync")); 053 } 054 055 public IpcNamedLockFactory(Path ipcHome) { 056 requireNonNull(ipcHome); 057 Path repository = ipcHome.resolve("repository"); 058 Path logPath = ipcHome.resolve("log"); 059 this.client = new IpcClient(repository, logPath, null); 060 } 061 062 @Override 063 protected NamedLock doGetLock(Collection<NamedLockKey> keys) { 064 NamedLockKey key = NamedLockKey.of( 065 digestKeys(keys), 066 keys.stream() 067 .map(NamedLockKey::resources) 068 .flatMap(Collection::stream) 069 .collect(Collectors.toList())); 070 return getLockAndRefTrack( 071 key, 072 () -> new IpcNamedLock( 073 key, this, client, keys.stream().map(NamedLockKey::name).collect(Collectors.toList()))); 074 } 075 076 /** 077 * Computes the digest identifying given (ordered) collection of lock keys. The encoding is unambiguous — 078 * netstring-like, the key count followed by each name length-prefixed — so distinct key collections always 079 * yield distinct digests. Plainly concatenating the names would give boundary-shifted collections (for 080 * example {@code ["foo", "bar"]} vs {@code ["foob", "ar"]} — and lock names embed wire-supplied artifact 081 * coordinates) the same digest and hence the same lock identity, letting two racing processes mutate the 082 * same local repository paths while holding what they believe are different locks. 083 */ 084 static String digestKeys(Collection<NamedLockKey> keys) { 085 StringDigestUtil sha1 = StringDigestUtil.sha1(); 086 sha1.update(keys.size() + ":"); 087 for (NamedLockKey key : keys) { 088 String name = key.name(); 089 sha1.update(name.length() + ":"); 090 sha1.update(name); 091 } 092 return sha1.digest(); 093 } 094 095 @Override 096 protected NamedLockSupport createLock(NamedLockKey key) { 097 throw new IllegalStateException("should not get here"); 098 } 099 100 @Override 101 protected void doShutdown() { 102 client.close(); 103 } 104}