I am designing a distributed data warehouse that supports sharding and replication. Now let's suppose I have a manager application that maintains a "list" of all the database instances that belong to the data warehouse somewhere in a database table (in some "master" database) together with their type, e.g., Oracle, MySql, etc., and credentials, e.g., server name, user name, password, database name, etc. (note that this could also be a distributed configuration service like Zookeeper). Let's also suppose that given one row x from this table, I want to be able to create a native DataSource
object for x at runtime, i.e., as the manager application needs it. What I need to do is reading the row for database x from the master database, get the type of the database, its credentials and create an instance of the DataSource
implementation provided by the vendor (I don't want to use wrappers such as DriverManagerDataSource
and the likes). I'd like to be able to add new JDBC drivers from configuration only, without recompiling.
Now here's my implementation idea. Given a list of N JDBC drivers, I could create N Spring prototype beans, one per DataSource
and then when my application needs an instance for database x, create a new instance from one of the N prototypes and then set the right credentials for x.
This is the tricky part: I don't want to have to write code to do the above for each different driver. I'd like somehow to put placeholders in my Spring prototype and then fill them at runtime with a map from the database fields to the native DataSource
setters for those. For example, I could have two prototypes such as:
<bean id="postgresDatasourcePrototype" scope="prototype"
class="org.postgresql.jdbc2.optional.SimpleDataSource">
<property name="serverName" value="${db.host}"/>
<property name="databaseName" value="${db.name}"/>
<property name="portNumber" value="${db.port}"/>
<property name="user" value="${db.user}"/>
<property name="password" value="${db.pwd}"/>
</bean>
<bean id="ingresDatasourcePrototype" scope="prototype"
class="com.ingres.jdbc.IngresDataSource">
<property name="serverName" value="${db.host}"/>
<property name="databaseName" value="${db.name}"/>
<property name="portName" value="${db.port}"/>
<property name="user" value="${db.user}"/>
<property name="password" value="${db.pwd}"/>
</bean>
and I would want to bind the placeholders with parameters from the row for x.
Notice that although the properties for most JDBC drivers are similar, (1) there is no common interface and (2) there can be variation, e.g., portNumber vs portName above. If there's no out-of-the-box way to do so using Spring, the only thing I could think of is to provide a map from the fields of x to the properties of the JDBC datasource for each driver (at configuration time) and then use reflection to actually set those properties, which is basically what Spring does under the cover.
EDIT: Just to make it clear, there can be N different JDBC drivers, and I might need to create M distinct instances of the data source for that driver, each with different credentials read from a different row x.
Note that I want a general solution, i.e., it should work for JMS ConnectionFactory
s as well.
Is there a way to do this using Spring? I was looking at PropertyPlaceholderConfigurer but it doesn't look like it fits my use case.
Thanks!
Giovanni