001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.util.concurrent; 019 020import java.util.concurrent.CountDownLatch; 021import java.util.concurrent.TimeoutException; 022import java.util.concurrent.atomic.AtomicReference; 023 024import net.sf.hajdbc.ExceptionFactory; 025import net.sf.hajdbc.Lifecycle; 026import net.sf.hajdbc.logging.Level; 027import net.sf.hajdbc.logging.Logger; 028import net.sf.hajdbc.logging.LoggerFactory; 029import net.sf.hajdbc.util.TimePeriod; 030 031/** 032 * @author Paul Ferraro 033 */ 034public class LifecycleRegistry<K, V extends Lifecycle, C, E extends Exception> implements Registry<K, V, C, E> 035{ 036 private final Logger logger = LoggerFactory.getLogger(this.getClass()); 037 private final LifecycleRegistry.Store<K, RegistryEntry> store; 038 039 final Factory<K, V, C, E> factory; 040 final ExceptionFactory<E> exceptionFactory; 041 042 public LifecycleRegistry(Factory<K, V, C, E> factory, RegistryStoreFactory<K> storeFactory, ExceptionFactory<E> exceptionFactory) 043 { 044 this.store = storeFactory.createStore(); 045 this.factory = factory; 046 this.exceptionFactory = exceptionFactory; 047 } 048 049 /** 050 * {@inheritDoc} 051 * @see net.sf.hajdbc.util.concurrent.Registry#get(java.lang.Object, java.lang.Object) 052 */ 053 @Override 054 public V get(K key, C context) throws E 055 { 056 RegistryEntry entry = this.store.get(key); 057 058 if (entry != null) 059 { 060 return entry.getValue(); 061 } 062 063 V value = this.factory.create(key, context); 064 065 entry = new RegistryEntry(value); 066 067 RegistryEntry existing = this.store.setIfAbsent(key, entry); 068 069 if (existing != null) 070 { 071 return existing.getValue(); 072 } 073 074 try 075 { 076 value.start(); 077 078 entry.started(); 079 080 return value; 081 } 082 catch (Exception e) 083 { 084 try 085 { 086 value.stop(); 087 } 088 catch (Exception re) 089 { 090 this.logger.log(Level.INFO, re); 091 } 092 093 this.store.clear(key); 094 095 throw this.exceptionFactory.createException(e); 096 } 097 } 098 099 /** 100 * {@inheritDoc} 101 * @see net.sf.hajdbc.util.concurrent.Registry#remove(java.lang.Object) 102 */ 103 @Override 104 public void remove(K key) throws E 105 { 106 RegistryEntry entry = this.store.clear(key); 107 108 if (entry != null) 109 { 110 entry.getValue().stop(); 111 } 112 } 113 114 private class RegistryEntry 115 { 116 private final V value; 117 private final AtomicReference<CountDownLatch> latchRef = new AtomicReference<CountDownLatch>(new CountDownLatch(1)); 118 119 RegistryEntry(V value) 120 { 121 this.value = value; 122 } 123 124 V getValue() throws E 125 { 126 CountDownLatch latch = this.latchRef.get(); 127 128 if (latch != null) 129 { 130 TimePeriod timeout = LifecycleRegistry.this.factory.getTimeout(); 131 try 132 { 133 if (!latch.await(timeout.getValue(), timeout.getUnit())) 134 { 135 throw LifecycleRegistry.this.exceptionFactory.createException(new TimeoutException()); 136 } 137 } 138 catch (InterruptedException e) 139 { 140 Thread.currentThread().interrupt(); 141 throw LifecycleRegistry.this.exceptionFactory.createException(e); 142 } 143 } 144 145 return this.value; 146 } 147 148 void started() 149 { 150 CountDownLatch latch = this.latchRef.getAndSet(null); 151 152 if (latch != null) 153 { 154 latch.countDown(); 155 } 156 } 157 } 158}