/** * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Andrea Aime - GeoSolutions */ package org.geowebcache.diskquota.jdbc; import java.io.File; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Arrays; import java.util.List; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.geowebcache.config.ConfigurationException; import org.geowebcache.config.ConfigurationResourceProvider; import org.geowebcache.config.XMLFileResourceProvider; import org.geowebcache.diskquota.QuotaStore; import org.geowebcache.diskquota.QuotaStoreFactory; import org.geowebcache.diskquota.jdbc.JDBCConfiguration.ConnectionPoolConfiguration; import org.geowebcache.diskquota.storage.TilePageCalculator; import org.geowebcache.storage.DefaultStorageFinder; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * Builds the quota store for the JDBC family (either H2 or JDBC) * * @author Andrea Aime - GeoSolutions * */ public class JDBCQuotaStoreFactory implements QuotaStoreFactory, ApplicationContextAware { private static final Log log = LogFactory.getLog(JDBCQuotaStore.class); private static final String CONFIGURATION_FILE_NAME = "geowebcache-diskquota-jdbc.xml"; public static final String H2_STORE = "H2"; public static final String JDBC_STORE = "JDBC"; private ApplicationContext appContext; private ConfigurationResourceProvider defaultResourceProvider; public JDBCQuotaStoreFactory() {} public JDBCQuotaStoreFactory(final ConfigurationResourceProvider resourceProvider) { this.defaultResourceProvider = resourceProvider; } public List<String> getSupportedStoreNames() { return Arrays.asList(H2_STORE, JDBC_STORE); } public QuotaStore getQuotaStore(ApplicationContext ctx, String quotaStoreName) throws ConfigurationException { // lookup dependencies in the classpath DefaultStorageFinder cacheDirFinder = (DefaultStorageFinder) ctx .getBean("gwcDefaultStorageFinder"); TilePageCalculator tilePageCalculator = (TilePageCalculator) ctx .getBean("gwcTilePageCalculator"); DataSource ds = null; if (H2_STORE.equals(quotaStoreName)) { return initializeH2Store(cacheDirFinder, tilePageCalculator); } else if (JDBC_STORE.equals(quotaStoreName)) { return getJDBCStore(cacheDirFinder, tilePageCalculator); } return null; } private ConfigurationResourceProvider createResourceProvider(DefaultStorageFinder cacheDirFinder) throws ConfigurationException { return new XMLFileResourceProvider(CONFIGURATION_FILE_NAME, (org.springframework.web.context.WebApplicationContext) appContext, cacheDirFinder); } private QuotaStore getJDBCStore(DefaultStorageFinder cacheDirFinder, TilePageCalculator tilePageCalculator) throws ConfigurationException { JDBCConfiguration config = null; ConfigurationResourceProvider resourceProvider = defaultResourceProvider == null ? createResourceProvider(cacheDirFinder) : defaultResourceProvider; try { if (!resourceProvider.hasInput()) { throw new IllegalArgumentException("Unable to read JDBC configuration file: " + resourceProvider.getLocation()); } config = JDBCConfiguration.load(resourceProvider.in()); } catch (IOException e) { throw new IllegalArgumentException("Failed to read JDBC configuration file at " + resourceProvider.getId()); } return getJDBCStore(cacheDirFinder, tilePageCalculator, config); } public QuotaStore getJDBCStore(ApplicationContext ctx, JDBCConfiguration config) throws ConfigurationException { // lookup dependencies in the classpath DefaultStorageFinder cacheDirFinder = (DefaultStorageFinder) ctx .getBean("gwcDefaultStorageFinder"); TilePageCalculator tilePageCalculator = (TilePageCalculator) ctx .getBean("gwcTilePageCalculator"); return getJDBCStore(cacheDirFinder, tilePageCalculator, config); } private QuotaStore getJDBCStore(DefaultStorageFinder cacheDirFinder, TilePageCalculator tilePageCalculator, JDBCConfiguration config) throws ConfigurationException { JDBCConfiguration expandedConfig = config.clone(true); DataSource ds = getDataSource(expandedConfig); // prepare the dialect String dialectName = expandedConfig.getDialect(); String dialectBeanName = dialectName + "QuotaDialect"; Object bean = appContext.getBean(dialectBeanName); if(bean == null) { throw new ConfigurationException("Could not locate bean " + dialectBeanName + " for dialect " + dialectName + " in the Spring application context"); } else if(!(bean instanceof SQLDialect)) { throw new ConfigurationException("Bean " + dialectBeanName + " for dialect " + dialectName + " was found in the Spring application " + "context, but it's not a SQLDialect object, instead it's a " + bean.getClass().getName()); } SQLDialect dialect = (SQLDialect) bean; // build up the store JDBCQuotaStore store = new JDBCQuotaStore(cacheDirFinder, tilePageCalculator); store.setDataSource(ds); store.setDialect(dialect); // sets schema if configured in geowebcache-diskquota-jdbc.xml store.setSchema(expandedConfig.getSchema()); // initialize it store.initialize(); return store; } private DataSource getDataSource(JDBCConfiguration config) throws ConfigurationException { try { DataSource ds = null; if (config.getJNDISource() != null) { InitialContext context = new InitialContext(); ds = (DataSource) context.lookup(config.getJNDISource()); } else if(config.getConnectionPool() != null){ ConnectionPoolConfiguration cp = config.getConnectionPool(); BasicDataSource bds = new BasicDataSource(); bds.setDriverClassName(cp.getDriver()); bds.setUrl(cp.getUrl()); bds.setUsername(cp.getUsername()); bds.setPassword(cp.getPassword()); bds.setPoolPreparedStatements(true); bds.setMaxOpenPreparedStatements(cp.getMaxOpenPreparedStatements()); bds.setMinIdle(cp.getMinConnections()); bds.setMaxActive(cp.getMaxConnections()); bds.setMaxWait(cp.getConnectionTimeout() * 1000); bds.setValidationQuery(cp.getValidationQuery()); ds = bds; } // verify the datasource works Connection c = null; try { c = ds.getConnection(); } catch(SQLException e) { throw new ConfigurationException("Failed to get a database connection: " + e.getMessage(), e); } finally { if(c != null) { try { c.close(); } catch(SQLException e) { // nothing we can do about it, but at least let the admin know log.debug("An error occurred while closing the test JDBC connection: " + e.getMessage(), e); } } } return ds; } catch (NamingException e) { throw new ConfigurationException("Failed to locate the data source in JNDI", e); } } private QuotaStore initializeH2Store(DefaultStorageFinder cacheDirFinder, TilePageCalculator tilePageCalculator) throws ConfigurationException { // get a default data source located in the cache directory DataSource ds = getH2DataSource(cacheDirFinder); // build up the store JDBCQuotaStore store = new JDBCQuotaStore(cacheDirFinder, tilePageCalculator); store.setDataSource(ds); store.setDialect(new H2Dialect()); // initialize it store.initialize(); return store; } /** * Prepares a simple data source for the embedded H2 * * @param cacheDirFinder * @return * @throws ConfigurationException */ private DataSource getH2DataSource(DefaultStorageFinder cacheDirFinder) throws ConfigurationException { File storeDirectory = new File(cacheDirFinder.getDefaultPath(), "diskquota_page_store_h2"); storeDirectory.mkdirs(); BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("org.h2.Driver"); String database = new File(storeDirectory, "diskquota").getAbsolutePath(); dataSource.setUrl("jdbc:h2:" + database); dataSource.setUsername("sa"); dataSource.setPoolPreparedStatements(true); dataSource.setAccessToUnderlyingConnectionAllowed(true); dataSource.setMinIdle(1); dataSource.setMaxActive(-1); // boundless dataSource.setMaxWait(5000); return dataSource; } public void setApplicationContext(ApplicationContext appContext) throws BeansException { this.appContext = appContext; } }