Java tutorial
/* * WebTop Services is a Web Application framework developed by Sonicle S.r.l. * Copyright (C) 2014 Sonicle S.r.l. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License version 3 as published by * the Free Software Foundation with the addition of the following permission * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED * WORK IN WHICH THE COPYRIGHT IS OWNED BY SONICLE, SONICLE DISCLAIMS THE * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * 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 Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA. * * You can contact Sonicle S.r.l. at email address sonicle@sonicle.com * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License * version 3, these Appropriate Legal Notices must retain the display of the * Sonicle logo and Sonicle copyright notice. If the display of the logo is not * reasonably feasible for technical reasons, the Appropriate Legal Notices must * display the words "Copyright (C) 2014 Sonicle S.r.l.". */ package com.sonicle.webtop.core.app; import com.sonicle.commons.PathUtils; import com.sonicle.webtop.core.app.DataSourcesConfig.HikariConfigMap; import com.sonicle.webtop.core.sdk.WTRuntimeException; import com.sonicle.webtop.core.sdk.interfaces.IConnectionProvider; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.io.File; import java.net.URL; import java.sql.Connection; import java.sql.SQLException; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map.Entry; import javax.sql.DataSource; import net.sf.qualitycheck.Check; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; /** * * @author malbinola */ public class ConnectionManager implements IConnectionProvider { private static final Logger logger = WT.getLogger(ConnectionManager.class); private static boolean initialized = false; /** * Initialization method. This method should be called once. * * @param wta WebTopApp instance. * @param configPath The optional path in which to look for configuration. * @return The instance. */ public static synchronized ConnectionManager initialize(WebTopApp wta, String configPath) { if (initialized) throw new RuntimeException("Initialization already done"); ConnectionManager conm = new ConnectionManager(wta, configPath); initialized = true; logger.info("Initialized"); return conm; } public static final String CONFIG_NAME = "data-sources.xml"; public static final String DEFAULT_CONFIG_RESOURCE_PATH = "/META-INF/" + CONFIG_NAME; public static final String DEFAULT_DATASOURCE = "default"; private boolean shutdown = false; private WebTopApp wta = null; private DataSourcesConfig config = null; private final HashMap<String, HikariDataSource> pools = new HashMap<>(); /** * Private constructor. * Instances of this class must be created using static initialize method. * @param wta WebTopApp instance. */ private ConnectionManager(WebTopApp wta, String configPath) { this.wta = wta; init(configPath); } /** * Performs cleanup process. */ void cleanup() { synchronized (pools) { shutdown = true; for (HikariDataSource pool : pools.values()) { pool.close(); } pools.clear(); } wta = null; logger.info("Cleaned up"); } private void init(String webappsConfigPath) { String path = null; File file = null; // Loads dataSources configuration if (!StringUtils.isBlank(webappsConfigPath)) { path = PathUtils.concatPaths(webappsConfigPath, CONFIG_NAME); file = new File(path); } if ((file == null) || !file.exists()) { path = wta.getContextResourcePath(DEFAULT_CONFIG_RESOURCE_PATH); file = new File(path); } if (file == null) throw new WTRuntimeException("Configuration file not found [{0}]", path); config = new DataSourcesConfig(); try { logger.debug("Loading dataSources configuration at [{}]", path); config.parseConfiguration(file); } catch (Exception ex) { throw new RuntimeException("Unable to load dataSources configuration file", ex); } /* String configResource = "/META-INF/data-sources.xml"; config = new DataSourcesConfig(); try { logger.debug("Loading dataSources configuration at [{}]", path); URL url = wta.getContextResource(configResource); config.parseConfiguration(url); } catch(Exception ex) { throw new RuntimeException("Unable to load dataSources configuration file", ex); } */ // Setup core sources logger.debug("Setting-up core dataSources..."); HikariConfigMap coreSources = config.getSources(CoreManifest.ID); if (!coreSources.containsKey(DEFAULT_DATASOURCE)) { throw new RuntimeException("No core default dataSource defined"); } for (Entry<String, HikariConfig> entry : coreSources.entrySet()) { registerDataSource(CoreManifest.ID, entry.getKey(), entry.getValue()); } } public final void registerDataSource(String namespace, String dataSourceName, HikariConfig config) { logger.debug("Registering data source [{}] into namespace [{}]", dataSourceName, namespace); addPool(poolName(namespace, dataSourceName), config); } /* public void registerJdbc3DataSource(String name, String driverClassName, String jdbcUrl, String username, String password) throws SQLException { HikariConfig config = new HikariConfig(); config.setDriverClassName(driverClassName); config.setJdbcUrl(jdbcUrl); if(username != null) config.setUsername(username); if(password != null) config.setPassword(password); logger.debug("Registering jdbc3 data source [{}]", name); logger.trace("[{}, {}, {}]", driverClassName, jdbcUrl, username); addPool(name, config); } public void registerJdbc4DataSource(String poolName, String dataSourceClassName, String serverName, Integer serverPort, String databaseName, String user, String password) throws SQLException { HikariConfig config = new HikariConfig(); config.setDataSourceClassName(dataSourceClassName); config.addDataSourceProperty("serverName", serverName); if(serverPort != null) config.addDataSourceProperty("port", serverPort); config.addDataSourceProperty("databaseName", databaseName); config.addDataSourceProperty("user", user); if(password != null) config.addDataSourceProperty("password", password); logger.debug("Registering jdbc4 data source [{}]", poolName); logger.trace("[{}, {}, {}, {}, {}]", dataSourceClassName, serverName, serverPort, databaseName, user); addPool(poolName, config); } */ public DataSourcesConfig getConfiguration() { return config; } /** * Builds a valid connection pool name. * @param namespace The pool namespace (eg. the service ID). * @param dataSourceName The data source name (eg. default) * @return Concatenated name */ public String poolName(String namespace, String dataSourceName) { return namespace + "." + dataSourceName; } /** * Returns the default Core DataSource. * @return DataSource object. * @throws SQLException */ public DataSource getDataSource() throws SQLException { return getDataSource(CoreManifest.ID, DEFAULT_DATASOURCE); } /** * Returns the default DataSource from desired namespace. * @param namespace The pool namespace. * @return DataSource object. * @throws SQLException */ public DataSource getDataSource(String namespace) throws SQLException { return getDataSource(namespace, DEFAULT_DATASOURCE); } /** * Returns a DataSource from desired namespace. * @param namespace The pool namespace. * @param dataSourceName The dataSource name. * @return DataSource object. * @throws SQLException */ public DataSource getDataSource(String namespace, String dataSourceName) throws SQLException { return getPool(poolName(namespace, dataSourceName)); } /** * Return the default Core connection. * @return A ready Connection object. * @throws SQLException */ @Override public Connection getConnection() throws SQLException { return getConnection(CoreManifest.ID); } /** * Return the default Core connection. * @param autoCommit False to disable auto-commit mode; defaults to True. * @return A ready Connection object. * @throws SQLException */ public Connection getConnection(boolean autoCommit) throws SQLException { return getConnection(CoreManifest.ID, autoCommit); } /** * Returns the default connection from desired namespace. * @param namespace The pool namespace. * @return A ready Connection object. * @throws SQLException */ @Override public Connection getConnection(String namespace) throws SQLException { return getConnection(namespace, DEFAULT_DATASOURCE); } /** * Returns the default connection from desired namespace. * @param namespace The pool namespace. * @param autoCommit False to disable auto-commit mode; defaults to True. * @return A ready Connection object. * @throws SQLException */ public Connection getConnection(String namespace, boolean autoCommit) throws SQLException { return getConnection(namespace, DEFAULT_DATASOURCE, autoCommit); } /** * Returns a connection from desired namespace. * @param namespace The pool namespace. * @param dataSourceName The dataSource name. * @return A ready Connection object. * @throws SQLException */ @Override public Connection getConnection(String namespace, String dataSourceName) throws SQLException { return getConnection(namespace, dataSourceName, true); } /** * Returns a connection from desired namespace. * @param namespace The pool namespace. * @param dataSourceName The dataSource name. * @param autoCommit False to disable auto-commit mode; defaults to True. * @return A ready Connection object. * @throws SQLException */ public Connection getConnection(String namespace, String dataSourceName, boolean autoCommit) throws SQLException { Check.notNull(namespace); Check.notNull(dataSourceName); String poolName = poolName(namespace, dataSourceName); Connection con = getPool(poolName).getConnection(); con.setAutoCommit(autoCommit); return con; } /** * Returns the default connection from desired namespace (with fallback). * @param namespace The pool namespace. * @return A ready Connection object. * @throws SQLException */ public Connection getFallbackConnection(String namespace) throws SQLException { return getFallbackConnection(namespace, DEFAULT_DATASOURCE); } /** * Returns the default connection from desired namespace (with fallback). * @param namespace The pool namespace. * @param autoCommit False to disable auto-commit mode; defaults to True. * @return A ready Connection object. * @throws SQLException */ public Connection getFallbackConnection(String namespace, boolean autoCommit) throws SQLException { return getFallbackConnection(namespace, DEFAULT_DATASOURCE, autoCommit); } /** * Returns a connection from desired namespace (with fallback). * @param namespace The pool namespace. * @param dataSourceName The dataSource name. * @return A ready Connection object. * @throws SQLException */ public Connection getFallbackConnection(String namespace, String dataSourceName) throws SQLException { return getFallbackConnection(namespace, dataSourceName, true); } /** * Returns a connection from desired namespace (with fallback). * @param namespace The pool namespace. * @param dataSourceName The dataSource name. * @param autoCommit False to disable auto-commit mode; defaults to True. * @return A ready Connection object. * @throws SQLException */ public Connection getFallbackConnection(String namespace, String dataSourceName, boolean autoCommit) throws SQLException { Check.notNull(namespace); Check.notNull(dataSourceName); String poolName = poolName(namespace, dataSourceName); if (isRegistered(namespace, dataSourceName)) { return getConnection(namespace, dataSourceName, autoCommit); } else { return getConnection(autoCommit); } } private void addPool(String poolName, HikariConfig config) { synchronized (pools) { if (shutdown) throw new RuntimeException("Manager is shutting down"); if (pools.containsKey(poolName)) throw new RuntimeException(MessageFormat.format("Pool for [{0}] is already defined.", poolName)); config.setPoolName(poolName); pools.put(poolName, new HikariDataSource(config)); } } private HikariDataSource getPool(String name) { synchronized (pools) { if (shutdown) throw new RuntimeException("Manager is shutting down"); if (!pools.containsKey(name)) throw new RuntimeException(MessageFormat.format("Pool [{0}] not found", name)); return pools.get(name); } } public boolean isRegistered(String namespace, String dataSourceName) { return isRegistered(poolName(namespace, dataSourceName)); } private boolean isRegistered(String poolName) { synchronized (pools) { if (shutdown) throw new RuntimeException("Manager is shutting down"); return pools.containsKey(poolName); } } /* public boolean isRegistered(String poolName) { synchronized(pools) { if(shutdown) throw new RuntimeException("Manager is shutting down"); return pools.containsKey(poolName); } } */ /* private void addPool(String name, String driverClassName, String jdbcUrl, String username, String password) throws SQLException { synchronized(pools) { if(pools.containsKey(name)) throw new RuntimeException(); HikariConfig config = new HikariConfig(); config.setDriverClassName(driverClassName); config.setJdbcUrl(jdbcUrl); if(username != null) config.setUsername(username); if(password != null) config.setPassword(password); config.setMaximumPoolSize(5); config.setMaximumPoolSize(20); pools.put(name, new HikariDataSource(config)); } } */ }