blob: bf4bfbad7176260a05481041ca944959c51ac5cd [file] [log] [blame]
/*
* 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.servicemix.kernel.main;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
/**
* Represents an exclusive lock on a database,
* used to avoid multiple SMX instances attempting
* to become master.
*
* @version $Revision: $
*/
public class DefaultJDBCLock implements Lock {
private static final String PROPERTY_LOCK_URL = "servicemix.lock.jdbc.url";
private static final String PROPERTY_LOCK_JDBC_DRIVER = "servicemix.lock.jdbc.driver";
private static final String PROPERTY_LOCK_JDBC_USER = "servicemix.lock.jdbc.user";
private static final String PROPERTY_LOCK_JDBC_PASSWORD = "servicemix.lock.jdbc.password";
private static final String PROPERTY_LOCK_JDBC_TABLE = "servicemix.lock.jdbc.table";
private static final String PROPERTY_LOCK_JDBC_CLUSTERNAME = "servicemix.lock.jdbc.clustername";
private static final String PROPERTY_LOCK_JDBC_TIMEOUT = "servicemix.lock.jdbc.timeout";
private final Statements statements;
private Connection lockConnection;
private String url;
private String driver;
private String user;
private String password;
private String table;
private String clusterName;
private int timeout;
public DefaultJDBCLock(Properties props) {
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 = "SERVICEMIX_LOCK"; }
if ( clusterName == null) { clusterName = "smx4"; }
this.statements = new Statements(table, clusterName);
if (time != null) {
this.timeout = Integer.parseInt(time) * 1000;
} else {
this.timeout = 10000; // 10 seconds
}
if (user == null) { user = ""; }
if (password == null) { password = ""; }
try {
obtainLock();
} catch (Exception e) {
System.err.println("Error occured while attempting to obtain connection: " + e.getMessage());
}
}
/**
* obtainLock - obtain the lock connection.
*
* @throws Exception
*/
private void obtainLock() throws Exception {
PreparedStatement statement = null;
while (true) {
try {
lockConnection = getConnection(driver, url, user, password);
lockConnection.setAutoCommit(false);
statements.init(lockConnection);
String sql = statements.testLockTableStatus();
statement = lockConnection.prepareStatement(sql);
statement.execute();
break;
} catch (Exception e) {
System.err.println("Could not obtain lock: " + e.getMessage());
Thread.sleep(this.timeout);
} finally {
if (null != statement) {
try {
statement.close();
} catch (SQLException e1) {
System.err.println("Caught while closing statement: " + e1.getMessage());
}
statement = null;
}
}
Thread.sleep(this.timeout);
}
System.out.println("Connected to data source: " + url);
}
/**
* 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 (lockConnection.isClosed()) { obtainLock(); }
long time = System.currentTimeMillis();
statement = lockConnection.prepareStatement(statements.getLockUpdateStatement(time));
int rows = statement.executeUpdate();
if (rows == 1) {
result=true;
}
} catch (Exception e) {
System.err.println("Failed to acquire database lock: " + e.getMessage());
}finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
System.err.println("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();
}
}
/**
* isAlive - test if lock still exists.
*/
public boolean isAlive() throws Exception {
if (lockConnection == null) { return false; }
PreparedStatement statement = null;
try {
lockConnection.setAutoCommit(false);
statements.init(lockConnection);
String sql = statements.testLockTableStatus();
statement = lockConnection.prepareStatement(sql);
statement.execute();
} catch (Exception ex) {
return false;
}
return true;
}
/**
* 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 + ";create=true", username, password);
} catch (Exception e) {
throw e;
}
return conn;
}
}