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.support; 020 021import java.util.ArrayDeque; 022import java.util.Collections; 023import java.util.Deque; 024import java.util.Map; 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.concurrent.TimeUnit; 027 028import org.eclipse.aether.named.NamedLock; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032/** 033 * Support class for {@link NamedLock} implementations providing reference counting. 034 */ 035public abstract class NamedLockSupport implements NamedLock { 036 protected final Logger logger = LoggerFactory.getLogger(getClass()); 037 038 private final String name; 039 040 private final NamedLockFactorySupport factory; 041 042 private final ConcurrentHashMap<Thread, Deque<String>> diagnosticState; // non-null only if diag enabled 043 044 public NamedLockSupport(final String name, final NamedLockFactorySupport factory) { 045 this.name = name; 046 this.factory = factory; 047 this.diagnosticState = factory.isDiagnosticEnabled() ? new ConcurrentHashMap<>() : null; 048 } 049 050 @Override 051 public String name() { 052 return name; 053 } 054 055 @Override 056 public boolean lockShared(long time, TimeUnit unit) throws InterruptedException { 057 if (diagnosticState != null) { 058 diagnosticState 059 .computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>()) 060 .push("shared"); 061 } 062 return doLockShared(time, unit); 063 } 064 065 protected abstract boolean doLockShared(long time, TimeUnit unit) throws InterruptedException; 066 067 @Override 068 public boolean lockExclusively(long time, TimeUnit unit) throws InterruptedException { 069 if (diagnosticState != null) { 070 diagnosticState 071 .computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>()) 072 .push("exclusive"); 073 } 074 return doLockExclusively(time, unit); 075 } 076 077 protected abstract boolean doLockExclusively(long time, TimeUnit unit) throws InterruptedException; 078 079 @Override 080 public void unlock() { 081 doUnlock(); 082 if (diagnosticState != null) { 083 diagnosticState 084 .computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>()) 085 .pop(); 086 } 087 } 088 089 protected abstract void doUnlock(); 090 091 @Override 092 public void close() { 093 doClose(); 094 } 095 096 protected void doClose() { 097 factory.closeLock(name); 098 } 099 100 /** 101 * Returns the diagnostic state (if collected) or empty map, never {@code null}. 102 * 103 * @since 1.9.11 104 */ 105 public Map<Thread, Deque<String>> diagnosticState() { 106 if (diagnosticState != null) { 107 return diagnosticState; 108 } else { 109 return Collections.emptyMap(); 110 } 111 } 112 113 @Override 114 public String toString() { 115 return getClass().getSimpleName() + "{" + "name='" + name + '\'' + '}'; 116 } 117}