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}