001    
002    /*
003     * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004     *
005     * $Id: AbstractUpdatingDataSource.java 111 2011-07-23 17:01:36Z archie.cobbs $
006     */
007    
008    package org.dellroad.stuff.schema;
009    
010    import java.io.PrintWriter;
011    import java.sql.Connection;
012    import java.sql.SQLException;
013    
014    import javax.sql.DataSource;
015    
016    /**
017     * A {@link DataSource} that wraps an inner {@link DataSource} and automatically performs some update
018     * operation on the inner {@link DataSource} on first access.
019     *
020     * <p>
021     * The {@link #setDataSource dataSource} property is required.
022     */
023    public abstract class AbstractUpdatingDataSource implements DataSource {
024    
025        private DataSource dataSource;
026        private boolean updated;
027    
028        /**
029         * Configure the underlying {@link DataSource}. Required property.
030         */
031        public void setDataSource(DataSource dataSource) {
032            this.dataSource = dataSource;
033        }
034    
035        /**
036         * Update the inner {@link DataSource}.
037         *
038         * <p>
039         * This method will be invoked at most once.
040         */
041        protected abstract void updateDataSource(DataSource dataSource) throws SQLException;
042    
043        /**
044         * Get the underlying {@link DataSource}.
045         */
046        protected DataSource getInnerDataSource() {
047            return this.dataSource;
048        }
049    
050        /**
051         * Determine if the underlying {@link DataSource} has been updated.
052         *
053         * <p>
054         * If the update is currently happening in another thread, the current thread will
055         * block until the update operation finishes.
056         */
057        protected synchronized boolean isUpdated() {
058            return this.updated;
059        }
060    
061        // DataSource methods
062    
063        @Override
064        public Connection getConnection() throws SQLException {
065            return getUpdatedDataSource().getConnection();
066        }
067    
068        @Override
069        public Connection getConnection(String username, String password) throws SQLException {
070            return getUpdatedDataSource().getConnection(username, password);
071        }
072    
073        @Override
074        public PrintWriter getLogWriter() throws SQLException {
075            return getUpdatedDataSource().getLogWriter();
076        }
077    
078        @Override
079        public void setLogWriter(PrintWriter pw) throws SQLException {
080            getUpdatedDataSource().setLogWriter(pw);
081        }
082    
083        @Override
084        public void setLoginTimeout(int timeout) throws SQLException {
085            getUpdatedDataSource().setLoginTimeout(timeout);
086        }
087    
088        @Override
089        public int getLoginTimeout() throws SQLException {
090            return getUpdatedDataSource().getLoginTimeout();
091        }
092    
093        @Override
094        public <T> T unwrap(Class<T> cl) throws SQLException {
095            return cl.cast(getUpdatedDataSource());
096        }
097    
098        @Override
099        public boolean isWrapperFor(Class<?> cl) throws SQLException {
100            return cl.isInstance(getUpdatedDataSource());
101        }
102    
103        // Internal methods
104    
105        private synchronized DataSource getUpdatedDataSource() throws SQLException {
106            if (!this.updated) {
107                if (this.dataSource == null)
108                    throw new IllegalArgumentException("no DataSource configured");
109                this.updateDataSource(this.dataSource);
110                this.updated = true;
111            }
112            return this.dataSource;
113        }
114    }
115