/* * Copyright (C) 2005-2008 Jive Software. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.database; import org.jivesoftware.util.JiveGlobals; import org.logicalcobwebs.proxool.ConnectionPoolDefinitionIF; import org.logicalcobwebs.proxool.ProxoolException; import org.logicalcobwebs.proxool.ProxoolFacade; import org.logicalcobwebs.proxool.admin.SnapshotIF; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * Default Jive connection provider, which uses an internal connection pool.<p> * * @author Jive Software */ public class DefaultConnectionProvider implements ConnectionProvider { private static final Logger Log = LoggerFactory.getLogger(DefaultConnectionProvider.class); private Properties settings; private String driver; private String serverURL; private String proxoolURL; private String username; private String password; private int minConnections = 3; private int maxConnections = 10; private int activeTimeout = 900000; // 15 minutes in milliseconds private String testSQL = ""; private Boolean testBeforeUse = true; private Boolean testAfterUse = true; /** * Maximum time a connection can be open before it's reopened (in days) */ private double connectionTimeout = 0.5; /** * MySQL doesn't currently support Unicode. However, a workaround is * implemented in the mm.mysql JDBC driver. Setting the Jive property * database.mysql.useUnicode to true will turn this feature on. */ private boolean mysqlUseUnicode; /** * Creates a new DefaultConnectionProvider. */ public DefaultConnectionProvider() { loadProperties(); } @Override public boolean isPooled() { return true; } @Override public Connection getConnection() throws SQLException { try { Class.forName("org.logicalcobwebs.proxool.ProxoolDriver"); return DriverManager.getConnection(proxoolURL, settings); } catch (ClassNotFoundException e) { throw new SQLException("DbConnectionProvider: Unable to find driver: "+e); } } @Override public void start() { proxoolURL = "proxool.openfire:"+getDriver()+":"+getServerURL(); settings = new Properties(); settings.setProperty("proxool.maximum-active-time", Integer.toString(activeTimeout)); settings.setProperty("proxool.maximum-connection-count", Integer.toString(getMaxConnections())); settings.setProperty("proxool.minimum-connection-count", Integer.toString(getMinConnections())); settings.setProperty("proxool.maximum-connection-lifetime", Integer.toString((int)(86400000 * getConnectionTimeout()))); settings.setProperty("proxool.test-before-use", testBeforeUse.toString()); settings.setProperty("proxool.test-after-use", testAfterUse.toString()); settings.setProperty("proxool.house-keeping-test-sql", testSQL); settings.setProperty("user", getUsername()); settings.setProperty("password", (getPassword() != null ? getPassword() : "")); } @Override public void restart() { } @Override public void destroy() { settings = null; } /** * Returns the JDBC driver classname used to make database connections. * For example: com.mysql.jdbc.Driver * * @return the JDBC driver classname. */ public String getDriver() { return driver; } /** * Sets the JDBC driver classname used to make database connections. * For example: com.mysql.jdbc.Driver * * @param driver the fully qualified JDBC driver name. */ public void setDriver(String driver) { this.driver = driver; saveProperties(); } /** * Returns the JDBC connection URL used to make database connections. * * @return the JDBC connection URL. */ public String getServerURL() { return serverURL; } /** * Sets the JDBC connection URL used to make database connections. * * @param serverURL the JDBC connection URL. */ public void setServerURL(String serverURL) { this.serverURL = serverURL; saveProperties(); } /** * Returns the username used to connect to the database. In some cases, * a username is not needed so this method will return null. * * @return the username used to connect to the datbase. */ public String getUsername() { return username; } /** * Sets the username used to connect to the database. In some cases, a * username is not needed so null should be passed in. * * @param username the username used to connect to the database. */ public void setUsername(String username) { this.username = username; saveProperties(); } /** * Returns the password used to connect to the database. In some cases, * a password is not needed so this method will return null. * * @return the password used to connect to the database. */ public String getPassword() { return password; } /** * Sets the password used to connect to the database. In some cases, a * password is not needed so null should be passed in. * * @param password the password used to connect to the database. */ public void setPassword(String password) { this.password = password; saveProperties(); } /** * Returns the minimum number of connections that the pool will use. This * should probably be at least three. * * @return the minimum number of connections in the pool. */ public int getMinConnections() { return minConnections; } /** * Sets the minimum number of connections that the pool will use. This * should probably be at least three. * * @param minConnections the minimum number of connections in the pool. */ public void setMinConnections(int minConnections) { this.minConnections = minConnections; saveProperties(); } /** * Returns the maximum number of connections that the pool will use. The * actual number of connections in the pool will vary between this value * and the minimum based on the current load. * * @return the max possible number of connections in the pool. */ public int getMaxConnections() { return maxConnections; } /** * Sets the maximum number of connections that the pool will use. The * actual number of connections in the pool will vary between this value * and the minimum based on the current load. * * @param maxConnections the max possible number of connections in the pool. */ public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; saveProperties(); } /** * Returns the amount of time between connection recycles in days. For * example, a value of .5 would correspond to recycling the connections * in the pool once every half day. * * @return the amount of time in days between connection recycles. */ public double getConnectionTimeout() { return connectionTimeout; } /** * Sets the amount of time between connection recycles in days. For * example, a value of .5 would correspond to recycling the connections * in the pool once every half day. * * @param connectionTimeout the amount of time in days between connection * recycles. */ public void setConnectionTimeout(double connectionTimeout) { this.connectionTimeout = connectionTimeout; saveProperties(); } /** * Returns the SQL statement used to test if a connection is valid. * * @return the SQL statement that will be run to test a connection. */ public String getTestSQL() { return testSQL; } /** * Sets the SQL statement used to test if a connection is valid. House keeping * and before/after connection tests make use of this. This * should be something that causes the minimal amount of work by the database * server and is as quick as possible. * * @param testSQL the SQL statement that will be run to test a connection. */ public void setTestSQL(String testSQL) { this.testSQL = testSQL; } /** * Returns whether returned connections will be tested before being handed over * to be used. * * @return True if connections are tested before use. */ public Boolean getTestBeforeUse() { return testBeforeUse; } /** * Sets whether connections will be tested before being handed over to be used. * * @param testBeforeUse True or false if connections are to be tested before use. */ public void setTestBeforeUse(Boolean testBeforeUse) { this.testBeforeUse = testBeforeUse; } /** * Returns whether returned connections will be tested after being returned to * the pool. * * @return True if connections are tested after use. */ public Boolean getTestAfterUse() { return testAfterUse; } /** * Sets whether connections will be tested after being returned to the pool. * * @param testAfterUse True or false if connections are to be tested after use. */ public void setTestAfterUse(Boolean testAfterUse) { this.testAfterUse = testAfterUse; } public boolean isMysqlUseUnicode() { return mysqlUseUnicode; } /** * Load properties that already exist from Jive properties. */ private void loadProperties() { driver = JiveGlobals.getXMLProperty("database.defaultProvider.driver"); serverURL = JiveGlobals.getXMLProperty("database.defaultProvider.serverURL"); username = JiveGlobals.getXMLProperty("database.defaultProvider.username"); password = JiveGlobals.getXMLProperty("database.defaultProvider.password"); String minCons = JiveGlobals.getXMLProperty("database.defaultProvider.minConnections"); String maxCons = JiveGlobals.getXMLProperty("database.defaultProvider.maxConnections"); String conTimeout = JiveGlobals.getXMLProperty("database.defaultProvider.connectionTimeout"); testSQL = JiveGlobals.getXMLProperty("database.defaultProvider.testSQL", DbConnectionManager.getTestSQL(driver)); testBeforeUse = JiveGlobals.getXMLProperty("database.defaultProvider.testBeforeUse", false); testAfterUse = JiveGlobals.getXMLProperty("database.defaultProvider.testAfterUse", false); // See if we should use Unicode under MySQL mysqlUseUnicode = Boolean.valueOf(JiveGlobals.getXMLProperty("database.mysql.useUnicode")); try { if (minCons != null) { minConnections = Integer.parseInt(minCons); } if (maxCons != null) { maxConnections = Integer.parseInt(maxCons); } if (conTimeout != null) { connectionTimeout = Double.parseDouble(conTimeout); } } catch (Exception e) { Log.error("Error: could not parse default pool properties. " + "Make sure the values exist and are correct.", e); } } /** * Save properties as Jive properties. */ private void saveProperties() { JiveGlobals.setXMLProperty("database.defaultProvider.driver", driver); JiveGlobals.setXMLProperty("database.defaultProvider.serverURL", serverURL); JiveGlobals.setXMLProperty("database.defaultProvider.username", username); JiveGlobals.setXMLProperty("database.defaultProvider.password", password); JiveGlobals.setXMLProperty("database.defaultProvider.testSQL", testSQL); JiveGlobals.setXMLProperty("database.defaultProvider.testBeforeUse", testBeforeUse.toString()); JiveGlobals.setXMLProperty("database.defaultProvider.testAfterUse", testAfterUse.toString()); JiveGlobals.setXMLProperty("database.defaultProvider.minConnections", Integer.toString(minConnections)); JiveGlobals.setXMLProperty("database.defaultProvider.maxConnections", Integer.toString(maxConnections)); JiveGlobals.setXMLProperty("database.defaultProvider.connectionTimeout", Double.toString(connectionTimeout)); } @Override public String toString() { try { ConnectionPoolDefinitionIF poolDef = ProxoolFacade.getConnectionPoolDefinition("openfire"); SnapshotIF poolStats = ProxoolFacade.getSnapshot("openfire", true); return poolDef.getMinimumConnectionCount()+","+poolDef.getMaximumConnectionCount()+"," +poolStats.getAvailableConnectionCount()+","+poolStats.getActiveConnectionCount(); } catch (ProxoolException e) { return "Default Connection Provider"; } } }