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