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 Deque<String> steps = null; 058 if (diagnosticState != null) { 059 steps = diagnosticState.computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>()); 060 } 061 if (steps != null) { 062 steps.push("wait-shared"); 063 } 064 boolean result = doLockShared(time, unit); 065 if (steps != null) { 066 steps.pop(); 067 if (result) { 068 steps.push("shared"); 069 } 070 } 071 return result; 072 } 073 074 protected abstract boolean doLockShared(long time, TimeUnit unit) throws InterruptedException; 075 076 @Override 077 public boolean lockExclusively(long time, TimeUnit unit) throws InterruptedException { 078 Deque<String> steps = null; 079 if (diagnosticState != null) { 080 steps = diagnosticState.computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>()); 081 } 082 if (steps != null) { 083 steps.push("wait-exclusive"); 084 } 085 boolean result = doLockExclusively(time, unit); 086 if (steps != null) { 087 steps.pop(); 088 if (result) { 089 steps.push("exclusive"); 090 } 091 } 092 return result; 093 } 094 095 protected abstract boolean doLockExclusively(long time, TimeUnit unit) throws InterruptedException; 096 097 @Override 098 public void unlock() { 099 doUnlock(); 100 if (diagnosticState != null) { 101 diagnosticState 102 .computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>()) 103 .pop(); 104 } 105 } 106 107 protected abstract void doUnlock(); 108 109 @Override 110 public void close() { 111 doClose(); 112 } 113 114 protected void doClose() { 115 factory.closeLock(name); 116 } 117 118 /** 119 * Returns the diagnostic state (if collected) or empty map, never {@code null}. 120 * 121 * @since 1.9.11 122 */ 123 public Map<Thread, Deque<String>> diagnosticState() { 124 if (diagnosticState != null) { 125 return diagnosticState; 126 } else { 127 return Collections.emptyMap(); 128 } 129 } 130 131 @Override 132 public String toString() { 133 return getClass().getSimpleName() + "{" + "name='" + name + '\'' + '}'; 134 } 135}