Skip to content

Latest commit

 

History

History
207 lines (158 loc) · 8.96 KB

Configuration.md

File metadata and controls

207 lines (158 loc) · 8.96 KB

Configuration and Dependencies.

JVM Version Requirement

Depot currently requires JDK 1.6 or greater.

Integrate with Ivy or Maven

Depot is published to the Maven Central repository and can be added as a dependency using the following configuration: com.samskivert:depot:1.7

This will automatically include the Google Guava and samskivert dependencies. You can add Ehcache as well via net.sf.ehcache:ehcache:1.6.0 (or a newer version, if available).

Manually Adding Dependencies

Depot depends on a small number of external libraries:

Depot also requires a JDBC driver for the database with which you plan to operate. Depot currently supports three database backends:

Configuration

The two main components that require configuration are the JDBC connection provider and the cache implementation.

StaticConnectionProvider

For testing and other simple systems that don't require connection pooling, the StaticConnectionProvider is a simple way to provide JDBC connections to Depot. It is used as follows (this example uses MySQL):

Properties props = new Properties();
// you'd probably load these properties from a file, but for the purposes
// of this example, we'll set them directly in the code
props.setProperty("default.driver", "com.mysql.jdbc.Driver");
props.setProperty("default.url", "jdbc:mysql://localhost:3306/dbname");
props.setProperty("default.username", "username");
props.setProperty("default.password", "password");

PersistenceContext perCtx = new PersistenceContext(
    "default", new StaticConnectionProvider(props), null);

DataSourceConnectionProvider

Production systems are more likely to use a JDBC DataSource to obtain their connections as those provide connection pooling and integrate with JNDI and such. The only non-obvious aspect of configuring Depot with a DataSource is that you can provide two datasources: one for read-only connections and one for read-write connections. Depot will obtain connections from the appropriate source depending on whether or not it is doing a query that is safe to be performed against a read-only mirror of your data or if it's doing a query that must talk to a database master.

What follows is a simple example of manually creating and configuring a Postgresql pooling DataSource:

PoolingDataSource readSource = new PoolingDataSource();
readSource.setDataSourceName("MyReadSource");
readSource.setServerName("myReadOnlyServerHost");
readSource.setDatabaseName("myDatabaseName");
readSource.setPortNumber(5432);
readSource.setUser("myUsername");
readSource.setPassword("myPassword");
readSource.setMaxConnections(4); // tune to your applications needs

PoolingDataSource writeSource = new PoolingDataSource();
writeSource.setDataSourceName("MyWriteSource");
writeSource.setServerName("myReadWriteServerHost");
writeSource.setDatabaseName("myDatabaseName");
writeSource.setPortNumber(5432);
writeSource.setUser("myUsername");
writeSource.setPassword("myPassword");
writeSource.setMaxConnections(1); // tune to your applications needs

PersistenceContext perCtx = new PersistenceContext(
    "notused", new DataSourceConnectionProvider("jdbc:postgresql", readSource, writeSource), null);

See the note below on lifecycle management.

EHCacheAdapter

You may have noticed the second argument to the PersistenceContext constructor in the above examples was always null. That is where the CacheAdapter is provided. By passing null, Depot will not use caching. Depot comes with integration for Ehcache and implementing additional cache integrations is as simple as implementing the CacheAdapter interface and supplying an instance to the PersistenceContext constructor.

The following example assumes that you have an ehcache.xml configuration file in your classpath. There are other ways to configure Ehcache but we'll leave that explanation to their documentation.

CacheManager cacheMgr = CacheManager.getInstance();
ConnectionProvider conProv = // ...
PersistenceContext perCtx = new PersistenceContext("ident", conProv, new EHCacheAdapter(cacheMgr));

See the note below on lifecycle management.

PersistenceContext Lifecycle

When your application is shutting down it should shutdown its PersistenceContext. However, to avoid integration headaches, Depot does not take responsibility for shutting down certain of its dependencies as those may be used by other parts of your application and you may wish to shut Depot down independently of these other components.

ConnectionProvider

Depot will shutdown its connection provider when the PersistenceContext is shutdown, however the two ConnectionProvider implementations have different shutdown behavior as explained below.

  • StaticConnectionProvider will close all JDBC Connection instances it has created when it is shutdown. If you are using Depot with StaticConnectionProvider you can simply shutdown your PersistenceContext and you're done.
  • DataSourceConnectionProvider will not shutdown its underlying DataSource implementations (indeed there is no API for doing so). As long as no queries are executing at the time that PersistenceContext is shutdown, then all JDBC Connection instances will have been closed and returned to the DataSource connection pool, so the application can shutdown its data sources in whatever way is appropriate.

CacheAdapter

Depot will shutdown its CacheAdapter when the PersistenceContext is shutdown, however the CacheAdapter implementation is free to do nothing in its shutdown call.

  • EHCacheAdapter does not shutdown its underlying CacheManager when it is shutdown to avoid conflict with other aspects of the application that may use Ehcache. Thus the application is responsible for shutting down the CacheManager itself when it is known to no longer be needed.

Injection

We use Guice around these parts for dependency injection. Using injection allows you to inject the PersistenceContext into your repository implementations:

@Singleton
public class FooRepository extends DepotRepository {
    @Inject public FooRepository (PersistenceContext perCtx) {
        super(perCtx);
    }
}

and then inject your repositories wherever you need them.

We also find the following pattern to be very effective:

public class FooModule extends AbstractModule {
    @Override protected void configure () {
        super.configure();
        // depot dependencies (we will initialize this persistence context later when the
        // server is ready to do database operations; not initializing it now ensures that no
        // one sneaks any database manipulations into the dependency resolution phase)
        bind(PersistenceContext.class).toInstance(new PersistenceContext());
    }
}

public class WhateverHandlesAppServerLifecycle {
    public void init () {
        // initialize our persistence context
        ConnectionProvider conProv = // ...
        _perCtx.init("ident", conProv, new EHCacheAdapter(_cacheMgr));

        // initialize our depot repositories; this runs all of our schema and data migrations
        _perCtx.initializeRepositories(true);
    }

    public void shutdown () {
        _perCtx.shutdown();
        _cacheMgr.shutdown();
    }

    @Inject protected PersistenceContext _perCtx;
    protected CacheManager _cacheMgr = CacheManager.getInstance();
}

One major benefit to the approach of delaying the initialization of your persistence context until the dependency resolution phase is complete is to ensure that no code accidentally (or intentionally) starts talking to the database during that phase. You almost certainly want to resolve all of your injection dependencies and then before you turn your application server loose, call initializeRepositories to cause all of your schema and data migrations to be run (or to fail and abort the initialization of your application).

If you don't call initializeRepositories then Depot will lazily initialize each PersistentRecord class when it is first accessed and run any schema migrations for that record. Data migrations will be disabled if you choose this lazily initialized approach.

Another note on initializeRepositories is that this will initialize all repositories that have been constructed with the supplied PersistenceContext up to that point. Any repositories constructed after initializeRepositories has been called will be initialized at that time (running schema and data migrations for their records) and a warning will be generated to alert you to this undesirable behavior. Again, experience has shown that you generally want to get all of your schema and data migrations out of the way immediately and before the application server starts normal operation.