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.redisson; 020 021import javax.inject.Named; 022import javax.inject.Singleton; 023 024import java.util.concurrent.ConcurrentHashMap; 025import java.util.concurrent.ConcurrentMap; 026import java.util.concurrent.TimeUnit; 027 028import org.eclipse.aether.named.NamedLock; 029import org.eclipse.aether.named.NamedLockKey; 030import org.eclipse.aether.named.support.AdaptedSemaphoreNamedLock; 031import org.redisson.api.RSemaphore; 032 033/** 034 * Provider of {@link RedissonSemaphoreNamedLockFactory} using Redisson and {@link org.redisson.api.RSemaphore}. 035 */ 036@Singleton 037@Named(RedissonSemaphoreNamedLockFactory.NAME) 038public class RedissonSemaphoreNamedLockFactory extends RedissonNamedLockFactorySupport { 039 public static final String NAME = "semaphore-redisson"; 040 041 private static final String TYPED_NAME_PREFIX = NAME_PREFIX + NAME + ":"; 042 043 private final ConcurrentMap<NamedLockKey, RSemaphore> semaphores; 044 045 public RedissonSemaphoreNamedLockFactory() { 046 this.semaphores = new ConcurrentHashMap<>(); 047 } 048 049 @Override 050 protected AdaptedSemaphoreNamedLock createLock(final NamedLockKey key) { 051 RSemaphore semaphore = semaphores.computeIfAbsent(key, k -> { 052 RSemaphore result = redissonClient.getSemaphore(TYPED_NAME_PREFIX + k.name()); 053 if (!result.trySetPermits(Integer.MAX_VALUE)) { 054 logger.warn("Failed to set permits on semaphore '{}'; it may be in a stale state", k.name()); 055 } 056 return result; 057 }); 058 return new AdaptedSemaphoreNamedLock(key, this, new RedissonSemaphore(semaphore)); 059 } 060 061 @Override 062 protected void destroyLock(final NamedLock namedLock) { 063 if (namedLock instanceof AdaptedSemaphoreNamedLock) { 064 final NamedLockKey key = namedLock.key(); 065 RSemaphore semaphore = semaphores.remove(key); 066 if (semaphore == null) { 067 throw new IllegalStateException("Semaphore expected, but does not exist: " + key); 068 } 069 /* There is no reasonable way to destroy the semaphore in Redis because we cannot know 070 * when the last process has stopped using it. 071 */ 072 } 073 } 074 075 private static final class RedissonSemaphore implements AdaptedSemaphoreNamedLock.AdaptedSemaphore { 076 private final RSemaphore semaphore; 077 078 private RedissonSemaphore(final RSemaphore semaphore) { 079 this.semaphore = semaphore; 080 } 081 082 @Override 083 public boolean tryAcquire(final int perms, final long time, final TimeUnit unit) throws InterruptedException { 084 return semaphore.tryAcquire(perms, time, unit); 085 } 086 087 @Override 088 public void release(final int perms) { 089 semaphore.release(perms); 090 } 091 } 092}