Author: gnodet Date: Wed Oct 14 07:22:47 2009 New Revision: 825040 URL: http://svn.apache.org/viewvc?rev=825040&view=rev Log: FELIX-1752: [karaf] extends DefaultJDBCLock to support MySQL out of the box
Added: felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/MySQLJDBCLock.java Modified: felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/Statements.java Added: felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/MySQLJDBCLock.java URL: http://svn.apache.org/viewvc/felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/MySQLJDBCLock.java?rev=825040&view=auto ============================================================================== --- felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/MySQLJDBCLock.java (added) +++ felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/MySQLJDBCLock.java Wed Oct 14 07:22:47 2009 @@ -0,0 +1,244 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.felix.karaf.main; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Properties; +import java.util.logging.Logger; + +/** + * Represents an exclusive lock on a database, + * used to avoid multiple Karaf instances attempting + * to become master. + * + * @version $Revision: $ + */ +public class MySQLJDBCLock implements Lock { + + private static final Logger LOG = Logger.getLogger(MySQLJDBCLock.class.getName()); + private static final String PROPERTY_LOCK_URL = "karaf.lock.jdbc.url"; + private static final String PROPERTY_LOCK_JDBC_DRIVER = "karaf.lock.jdbc.driver"; + private static final String PROPERTY_LOCK_JDBC_USER = "karaf.lock.jdbc.user"; + private static final String PROPERTY_LOCK_JDBC_PASSWORD = "karaf.lock.jdbc.password"; + private static final String PROPERTY_LOCK_JDBC_TABLE = "karaf.lock.jdbc.table"; + private static final String PROPERTY_LOCK_JDBC_CLUSTERNAME = "karaf.lock.jdbc.clustername"; + private static final String PROPERTY_LOCK_JDBC_TIMEOUT = "karaf.lock.jdbc.timeout"; + + private final Statements statements; + private Connection lockConnection; + private String url; + private String database; + private String driver; + private String user; + private String password; + private String table; + private String clusterName; + private int timeout; + + public MySQLJDBCLock(Properties props) { + LOG.addHandler(BootstrapLogManager.getDefaultHandler()); + this.url = props.getProperty(PROPERTY_LOCK_URL); + this.driver = props.getProperty(PROPERTY_LOCK_JDBC_DRIVER); + this.user = props.getProperty(PROPERTY_LOCK_JDBC_USER); + this.password = props.getProperty(PROPERTY_LOCK_JDBC_PASSWORD); + this.table = props.getProperty(PROPERTY_LOCK_JDBC_TABLE); + this.clusterName = props.getProperty(PROPERTY_LOCK_JDBC_CLUSTERNAME); + String time = props.getProperty(PROPERTY_LOCK_JDBC_TIMEOUT); + this.lockConnection = null; + if (table == null) { table = "KARAF_LOCK"; } + if ( clusterName == null) { clusterName = "karaf"; } + if (time != null) { + this.timeout = Integer.parseInt(time) * 1000; + } else { + this.timeout = 10000; // 10 seconds + } + if (user == null) { user = ""; } + if (password == null) { password = ""; } + + int db = props.getProperty(PROPERTY_LOCK_URL).lastIndexOf("/"); + this.url = props.getProperty(PROPERTY_LOCK_URL).substring(0, db); + this.database = props.getProperty(PROPERTY_LOCK_URL).substring(db +1); + this.statements = new Statements(database, table, clusterName); + testDB(); + } + + /** + * testDB - ensure specified database exists. + * + */ + private void testDB() { + try { + lockConnection = getConnection(driver, url, user, password); + lockConnection.setAutoCommit(false); + statements.init(lockConnection, database); + } catch (Exception e) { + LOG.severe("Error occured while attempting to obtain connection: " + e + " " + e.getMessage()); + } finally { + try { + lockConnection.close(); + lockConnection = null; + } catch (Exception f) { + LOG.severe("Error occured while cleaning up connection: " + f + " " + f.getMessage()); + } + } + } + + /** + * setUpdateCursor - Send Update directive to data base server. + * + * @throws Exception + */ + private boolean setUpdateCursor() throws Exception { + PreparedStatement statement = null; + boolean result = false; + try { + if ((lockConnection == null) || (lockConnection.isClosed())) { + LOG.fine("DefaultJDBCLock#setUpdateCursor:: connection: " + url + "/" + database ); + lockConnection = getConnection(driver, url + "/" + database, user, password); + lockConnection.setAutoCommit(false); + statements.init(lockConnection); + } else { + LOG.fine("MySQLJDBCLock#setUpdateCursor:: connection already established."); + return true; + } + String sql = "LOCK TABLES " + database + "." + table + " WRITE"; + statement = lockConnection.prepareStatement(sql); + result = statement.execute(); + } catch (Exception e) { + LOG.warning("Could not obtain connection: " + e.getMessage()); + } finally { + if (null != statement) { + try { + LOG.severe("Cleaning up DB connection."); + statement.close(); + } catch (SQLException e1) { + LOG.severe("Caught while closing statement: " + e1.getMessage()); + } + statement = null; + } + } + LOG.info("Connected to data source: " + url + " With RS: " + result); + return result; + } + + /** + * lock - a KeepAlive function to maintain lock. + * + * @return true if connection lock retained, false otherwise. + */ + public boolean lock() { + PreparedStatement statement = null; + boolean result = false; + try { + if (!setUpdateCursor()) { + LOG.severe("Could not set DB update cursor"); + return result; + } + LOG.fine("MySQLJDBCLock#lock:: have set Update Cursor, now do update"); + long time = System.currentTimeMillis(); + statement = lockConnection.prepareStatement(statements.getLockUpdateStatement(time)); + int rows = statement.executeUpdate(); + LOG.fine("MySQLJDBCLock#lock:: Number of update rows: " + rows); + if (rows >= 1) { + result=true; + } + } catch (Exception e) { + LOG.warning("Failed to acquire database lock: " + e.getMessage()); + }finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + LOG.severe("Failed to close statement" + e); + } + } + } + return result; + } + + /** + * release - terminate the lock connection safely. + */ + public void release() throws Exception { + if (lockConnection != null && !lockConnection.isClosed()) { + lockConnection.rollback(); + lockConnection.close(); + lockConnection = null; + } + } + + /** + * isAlive - test if lock still exists. + */ + public boolean isAlive() throws Exception { + if ((lockConnection == null) || (lockConnection.isClosed())) { + LOG.severe("Lost lock!"); + return false; + } + PreparedStatement statement = null; + boolean result = true; + try { + long time = System.currentTimeMillis(); + statement = lockConnection.prepareStatement(statements.getLockUpdateStatement(time)); + int rows = statement.executeUpdate(); + if (rows < 1) { + result = false; + } + } catch (Exception ex) { + LOG.severe("Error occured while testing lock: " + ex + " " + ex.getMessage()); + return false; + } finally { + if (statement != null) { + try { + statement.close(); + } catch (Exception ex1) { + LOG.severe("Error occured after testing lock: " + ex1.getMessage()); + } + } + } + return result; + } + + /** + * getConnection - Obtain connection to database via jdbc driver. + * + * @throws Exception + * @param driver, the JDBC driver class. + * @param url, url to data source. + * @param username, user to access data source. + * @param password, password for specified user. + * @return connection, null returned if conenction fails. + */ + private Connection getConnection(String driver, String url, + String username, String password) throws Exception { + Connection conn = null; + try { + Class.forName(driver); + conn = DriverManager.getConnection(url, username, password); + } catch (Exception e) { + LOG.severe("Error occured while setting up JDBC connection: " + e); + throw e; + } + return conn; + } + +} Modified: felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/Statements.java URL: http://svn.apache.org/viewvc/felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/Statements.java?rev=825040&r1=825039&r2=825040&view=diff ============================================================================== --- felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/Statements.java (original) +++ felix/trunk/karaf/main/src/main/java/org/apache/felix/karaf/main/Statements.java Wed Oct 14 07:22:47 2009 @@ -29,9 +29,10 @@ private static Logger LOG = Logger.getLogger(Statements.class.getName()); private String lockTableName = "KARAF_LOCK"; private String clusterName = "karaf"; + private String dbName = "sample"; private String lockCreateStatement; + private String lockDBCreateStatement; private String lockPopulateStatement; - private String lockUpdateStatement; public Statements(String tableName, String clusterName) { LOG.addHandler( BootstrapLogManager.getDefaultHandler() ); @@ -42,18 +43,51 @@ this.lockPopulateStatement="insert into " + lockTableName + " (TIME, CLUSTER) values (1, '" + clusterName + "')"; } + public Statements(String dbName, String tableName, String clusterName) { + LOG.addHandler( BootstrapLogManager.getDefaultHandler() ); + + this.dbName = dbName; + this.lockTableName = tableName; + this.clusterName = clusterName; + this.lockDBCreateStatement="create database if not exists " + dbName; + this.lockCreateStatement="create table " + lockTableName + " (TIME bigint, CLUSTER varchar(20)) ENGINE = INNODB"; + this.lockPopulateStatement="insert into " + lockTableName + " (TIME, CLUSTER) values (1, '" + clusterName + "')"; + } + + public String setUpdateCursor() { String test = "SELECT * FROM " + lockTableName + " FOR UPDATE"; return test; } public String getLockUpdateStatement(long timeStamp) { + String lockUpdateStatement = ""; lockUpdateStatement = "UPDATE " + lockTableName + " SET TIME=" + timeStamp + " WHERE CLUSTER = '" + clusterName + "'"; return lockUpdateStatement; } + public void init (Connection lockConnection, String dbName) { + Statement s = null; + try { + s = lockConnection.createStatement(); + s.execute(lockDBCreateStatement); + } catch (SQLException e) { + LOG.severe("SQL Exception: " + e + + " " + e.getMessage()); + } catch (Exception ignore) { + LOG.severe("Could not create database: " + ignore + + " " + ignore.getMessage()); + } finally { + try { + s.close(); + } catch (Throwable e) { + // ignore + } + } + } + /** * init - initialize db */