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.Deque; 022import java.util.HashMap; 023import java.util.Map; 024import java.util.concurrent.ConcurrentHashMap; 025import java.util.concurrent.ConcurrentMap; 026import java.util.concurrent.atomic.AtomicInteger; 027 028import org.eclipse.aether.named.NamedLockFactory; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import static java.util.Objects.requireNonNull; 033 034/** 035 * Support class for {@link NamedLockFactory} implementations providing reference counting. 036 */ 037public abstract class NamedLockFactorySupport implements NamedLockFactory { 038 /** 039 * System property key to enable locking diagnostic collection. 040 * 041 * @since 1.9.11 042 */ 043 private static final boolean DIAGNOSTIC_ENABLED = Boolean.getBoolean("aether.named.diagnostic.enabled"); 044 045 protected final Logger logger = LoggerFactory.getLogger(getClass()); 046 047 private final ConcurrentMap<String, NamedLockHolder> locks; 048 049 private final boolean diagnosticEnabled; 050 051 public NamedLockFactorySupport() { 052 this(DIAGNOSTIC_ENABLED); 053 } 054 055 public NamedLockFactorySupport(boolean diagnosticEnabled) { 056 this.locks = new ConcurrentHashMap<>(); 057 this.diagnosticEnabled = diagnosticEnabled; 058 } 059 060 /** 061 * Returns {@code true} if factory diagnostic collection is enabled. 062 * 063 * @since 1.9.11 064 */ 065 public boolean isDiagnosticEnabled() { 066 return diagnosticEnabled; 067 } 068 069 @Override 070 public NamedLockSupport getLock(final String name) { 071 return locks.compute(name, (k, v) -> { 072 if (v == null) { 073 v = new NamedLockHolder(createLock(k)); 074 } 075 v.incRef(); 076 return v; 077 }) 078 .namedLock; 079 } 080 081 @Override 082 public void shutdown() { 083 // override if needed 084 } 085 086 @Override 087 public <E extends Throwable> E onFailure(E failure) { 088 if (isDiagnosticEnabled()) { 089 Map<String, NamedLockHolder> locks = new HashMap<>(this.locks); // copy 090 int activeLocks = locks.size(); 091 logger.info("Diagnostic dump of lock factory"); 092 logger.info("==============================="); 093 logger.info("Implementation: {}", getClass().getName()); 094 logger.info("Active locks: {}", activeLocks); 095 logger.info(""); 096 if (activeLocks > 0) { 097 for (Map.Entry<String, NamedLockHolder> entry : locks.entrySet()) { 098 String name = entry.getKey(); 099 int refCount = entry.getValue().referenceCount.get(); 100 NamedLockSupport lock = entry.getValue().namedLock; 101 logger.info("Name: {}", name); 102 logger.info("RefCount: {}", refCount); 103 Map<Thread, Deque<String>> diag = lock.diagnosticState(); 104 diag.forEach((key, value) -> logger.info(" {} -> {}", key, value)); 105 } 106 logger.info(""); 107 } 108 } 109 return failure; 110 } 111 112 public void closeLock(final String name) { 113 locks.compute(name, (k, v) -> { 114 if (v != null && v.decRef() == 0) { 115 destroyLock(v.namedLock.name()); 116 return null; 117 } 118 return v; 119 }); 120 } 121 122 /** 123 * Implementations shall create and return {@link NamedLockSupport} for given {@code name}, this method must never 124 * return {@code null}. 125 */ 126 protected abstract NamedLockSupport createLock(String name); 127 128 /** 129 * Implementation may override this (empty) method to perform some sort of implementation specific cleanup for 130 * given lock name. Invoked when reference count for given name drops to zero and named lock was removed. 131 */ 132 protected void destroyLock(final String name) { 133 // override if needed 134 } 135 136 private static final class NamedLockHolder { 137 private final NamedLockSupport namedLock; 138 139 private final AtomicInteger referenceCount; 140 141 private NamedLockHolder(final NamedLockSupport namedLock) { 142 this.namedLock = requireNonNull(namedLock); 143 this.referenceCount = new AtomicInteger(0); 144 } 145 146 private int incRef() { 147 return referenceCount.incrementAndGet(); 148 } 149 150 private int decRef() { 151 return referenceCount.decrementAndGet(); 152 } 153 154 @Override 155 public String toString() { 156 return "[refCount=" + referenceCount.get() + ", lock=" + namedLock + "]"; 157 } 158 } 159}