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