/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev$
* Last modified by $Author$
* $Date$
*/
package tigase.conf;
//~--- non-JDK imports --------------------------------------------------------
import tigase.db.DataRepository;
import tigase.db.RepositoryFactory;
import tigase.db.TigaseDBException;
import tigase.util.DataTypes;
import static tigase.conf.Configurable.*;
//~--- JDK imports ------------------------------------------------------------
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
//~--- classes ----------------------------------------------------------------
/**
* Created: Dec 15, 2009 10:44:00 PM
*
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev$
*/
public class ConfigSQLRepository extends ConfigurationCache {
/** Field description */
public static final String CONFIG_REPO_URI_PROP_KEY = "tigase-config-repo-uri";
/** Field description */
public static final String CONFIG_REPO_URI_INIT_KEY = "--tigase-config-repo-uri";
/**
* Private logger for class instancess.
*/
private static final Logger log = Logger.getLogger(ConfigSQLRepository.class.getName());
//~--- fields ---------------------------------------------------------------
private JDBCAccess dbAccess = new JDBCAccess();
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param compName
* @param item
*/
@Override
public void addItem(String compName, ConfigItem item) {
dbAccess.addItem(item);
}
/**
* Method description
*
*
* @return
*
* @throws TigaseDBException
*/
@Override
public Collection<ConfigItem> allItems() throws TigaseDBException {
return dbAccess.getAllItems();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public String[] getCompNames() {
return dbAccess.getComponentNames();
}
/**
* Method description
*
*
* @param compName
* @param node
* @param key
*
* @return
*/
@Override
public ConfigItem getItem(String compName, String node, String key) {
return dbAccess.getItem(compName, node, key);
}
/**
* Method description
*
*
* @param compName
*
* @return
*/
@Override
public Set<ConfigItem> getItemsForComponent(String compName) {
return dbAccess.getCompItems(compName);
}
/**
* Method description
*
*
* @param compName
* @param node
*
* @return
*/
@Override
public String[] getKeys(String compName, String node) {
return dbAccess.getKeys(compName, node);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param params
*
* @throws ConfigurationException
*/
@Override
public void init(Map<String, Object> params) throws ConfigurationException {
String config_db_uri = System.getProperty(CONFIG_REPO_URI_PROP_KEY);
if (config_db_uri == null) {
config_db_uri = (String) params.get(CONFIG_REPO_URI_INIT_KEY);
}
if (config_db_uri == null) {
config_db_uri = (String) params.get(GEN_USER_DB_URI);
}
if (config_db_uri == null) {
log.severe("Missing configuration database connection string.");
log.severe("Tigase needs a database connection string to load configuration.");
log.severe("You can provide it in a few ways and the Tigase server checks");
log.severe("following parameters in the order below:");
log.severe("1. System property: -Dtigase-config-repo-uri=db-connection-string");
log.severe("2. init.properties file or command line parameter: "
+ "--tigase-config-repo-uri=db-connection-string");
log.severe("3. init.properties file or command line parameter: "
+ "--user-db-uri=db-connection-string");
log.severe("Please correct the error and restart the server.");
System.exit(1);
}
try {
dbAccess.initRepository(config_db_uri, null);
} catch (SQLException ex) {
log.log(Level.SEVERE, "Problem connecting to configuration database: ", ex);
log.severe("Please check whether the database connection string is correct: "
+ config_db_uri);
System.exit(1);
}
}
/**
* Method description
*
*
* @param compName
* @param item
*/
@Override
public void removeItem(String compName, ConfigItem item) {
dbAccess.removeItem(item);
}
/**
* Method description
*
*
* @return
*/
@Override
public int size() {
return dbAccess.getPropertiesCount();
}
//~--- inner classes --------------------------------------------------------
private class JDBCAccess {
/** Field description */
public static final String TABLE_NAME = "tigase_configuration";
private static final String CLUSTER_NODE_COLUMN = "cluster_node";
private static final String COMPONENT_NAME_COLUMN = "component_name";
private static final String NODE_NAME_COLUMN = "key_node";
private static final String KEY_NAME_COLUMN = "key_name";
private static final String VALUE_COLUMN = "value";
private static final String FLAG_COLUMN = "flag";
private static final String VALUE_TYPE_COLUMN = "value_type";
private static final String LAST_UPDATE_COLUMN = "last_update";
private static final String CREATE_TABLE_QUERY = "create table " + TABLE_NAME + " (" + " "
+ COMPONENT_NAME_COLUMN + " varchar(127) NOT NULL," + " " + KEY_NAME_COLUMN
+ " varchar(127) NOT NULL," + " " + VALUE_COLUMN + " varchar(8191) NOT NULL," + " "
+ CLUSTER_NODE_COLUMN + " varchar(255) NOT NULL DEFAULT ''," + " " + NODE_NAME_COLUMN
+ " varchar(127) NOT NULL DEFAULT ''," + " " + FLAG_COLUMN
+ " varchar(32) NOT NULL DEFAULT 'DEFAULT'," + " " + VALUE_TYPE_COLUMN
+ " varchar(8) NOT NULL DEFAULT 'S'," + " " + LAST_UPDATE_COLUMN + " timestamp,"
+ " primary key(" + CLUSTER_NODE_COLUMN + ", " + COMPONENT_NAME_COLUMN + ", "
+ NODE_NAME_COLUMN + ", " + KEY_NAME_COLUMN + ", " + FLAG_COLUMN + "))";
private static final String CLUSTER_NODE_WHERE_PART = " (" + CLUSTER_NODE_COLUMN + " = '' "
+ " OR " + CLUSTER_NODE_COLUMN + " = ?) ";
private static final String ITEM_WHERE_PART = " where " + CLUSTER_NODE_WHERE_PART + " AND ("
+ COMPONENT_NAME_COLUMN + " = ?) " + " AND (" + NODE_NAME_COLUMN + " = ?) " + " AND ("
+ KEY_NAME_COLUMN + " = ?) ";
// + " AND (" + FLAG_COLUMN + " = ?)";
private static final String CHECK_TABLE_QUERY = "select count(*) from " + TABLE_NAME;
private static final String GET_ITEM_QUERY = "select * from " + TABLE_NAME + ITEM_WHERE_PART;
private static final String ADD_ITEM_QUERY = "insert into " + TABLE_NAME + " ("
+ CLUSTER_NODE_COLUMN + ", " + COMPONENT_NAME_COLUMN + ", " + NODE_NAME_COLUMN + ", "
+ KEY_NAME_COLUMN + ", " + VALUE_COLUMN + ", " + VALUE_TYPE_COLUMN + ", " + FLAG_COLUMN
+ ") " + " values (?, ?, ?, ?, ?, ?, ?)";
private static final String UPDATE_ITEM_QUERY = "update " + TABLE_NAME + " set " + VALUE_COLUMN
+ " = ? " + " where (" + CLUSTER_NODE_COLUMN + " = ?) " + " AND (" + COMPONENT_NAME_COLUMN
+ " = ?) " + " AND (" + NODE_NAME_COLUMN + " = ?) " + " AND (" + KEY_NAME_COLUMN + " = ?)";
private static final String DELETE_ITEM_QUERY = "delete from " + TABLE_NAME + ITEM_WHERE_PART;
private static final String GET_ALL_ITEMS_QUERY = "select * from " + TABLE_NAME + " where "
+ CLUSTER_NODE_WHERE_PART;
private static final String GET_COMPONENT_ITEMS_QUERY = "select * from " + TABLE_NAME
+ " where " + CLUSTER_NODE_WHERE_PART + " AND (" + COMPONENT_NAME_COLUMN + " = ?)";
private static final String GET_UPDATED_ITEMS_QUERY = "select * from " + TABLE_NAME + " where "
+ CLUSTER_NODE_WHERE_PART + " AND (" + FLAG_COLUMN + " <> 'INITIAL')" + " AND ("
+ LAST_UPDATE_COLUMN + " > ?)";
private static final String GET_COMPONENT_NAMES_QUERY = "select distinct("
+ COMPONENT_NAME_COLUMN + ") from " + TABLE_NAME + " where " + CLUSTER_NODE_COLUMN;
private static final String GET_PROPERTIES_COUNT_QUERY = "select count(*) as count from "
+ TABLE_NAME + " where " + CLUSTER_NODE_COLUMN;
private static final String GET_KEYS_QUERY = "select " + KEY_NAME_COLUMN + " from "
+ TABLE_NAME + " where " + CLUSTER_NODE_WHERE_PART + " AND (" + COMPONENT_NAME_COLUMN
+ " = ?)" + " AND (" + NODE_NAME_COLUMN + " = ?)";
//~--- fields -------------------------------------------------------------
private DataRepository data_repo = null;
//~--- methods ------------------------------------------------------------
// private PreparedStatement createTableSt = null;
// private PreparedStatement checkTableSt = null;
// private PreparedStatement getItemSt = null;
// private PreparedStatement getAllItemsSt = null;
// private PreparedStatement getCompItemsSt = null;
// private PreparedStatement getUpdatedItemsSt = null;
// private PreparedStatement addItemSt = null;
// private PreparedStatement updateItemSt = null;
// private PreparedStatement deleteItemSt = null;
// private PreparedStatement getCompNamesSt = null;
// private PreparedStatement getPropertiesCountSt = null;
// private PreparedStatement getKeysSt = null;
/**
* Method description
*
*
* @param conn_str
* @param params
*
* @throws SQLException
*/
public void initRepository(String conn_str, Map<String, String> params) throws SQLException {
try {
data_repo = RepositoryFactory.getDataRepository(null, conn_str, params);
// Check if DB is correctly setup and contains all required tables.
checkDB();
data_repo.initPreparedStatement(GET_ITEM_QUERY, GET_ITEM_QUERY);
data_repo.initPreparedStatement(GET_ALL_ITEMS_QUERY, GET_ALL_ITEMS_QUERY);
data_repo.initPreparedStatement(GET_COMPONENT_ITEMS_QUERY, GET_COMPONENT_ITEMS_QUERY);
data_repo.initPreparedStatement(ADD_ITEM_QUERY, ADD_ITEM_QUERY);
data_repo.initPreparedStatement(UPDATE_ITEM_QUERY, UPDATE_ITEM_QUERY);
data_repo.initPreparedStatement(DELETE_ITEM_QUERY, DELETE_ITEM_QUERY);
data_repo.initPreparedStatement(GET_UPDATED_ITEMS_QUERY, GET_UPDATED_ITEMS_QUERY);
data_repo.initPreparedStatement(GET_COMPONENT_NAMES_QUERY, GET_COMPONENT_NAMES_QUERY);
data_repo.initPreparedStatement(GET_PROPERTIES_COUNT_QUERY, GET_PROPERTIES_COUNT_QUERY);
data_repo.initPreparedStatement(GET_KEYS_QUERY, GET_KEYS_QUERY);
} catch (Exception e) {}
}
private void addItem(ConfigItem item) {
try {
PreparedStatement addItemSt = data_repo.getPreparedStatement(null, ADD_ITEM_QUERY);
synchronized (addItemSt) {
addItemSt.setString(1, ((item.getClusterNode() != null) ? item.getClusterNode() : ""));
addItemSt.setString(2, item.getCompName());
addItemSt.setString(3, ((item.getNodeName() != null) ? item.getNodeName() : ""));
addItemSt.setString(4, item.getKeyName());
addItemSt.setString(5, item.getConfigValToString());
addItemSt.setString(6, "" + DataTypes.getTypeId(item.getConfigVal()));
addItemSt.setString(7, item.getFlag().name());
addItemSt.executeUpdate();
}
} catch (SQLException e) {
// Maybe the configuration item is already there, let's try to update it then
try {
PreparedStatement updateItemSt = data_repo.getPreparedStatement(null, UPDATE_ITEM_QUERY);
synchronized (updateItemSt) {
updateItemSt.setString(1, item.getConfigValToString());
updateItemSt.setString(2,
((item.getClusterNode() != null) ? item.getClusterNode() : ""));
updateItemSt.setString(3, item.getCompName());
updateItemSt.setString(4, ((item.getNodeName() != null) ? item.getNodeName() : ""));
updateItemSt.setString(5, item.getKeyName());
updateItemSt.executeUpdate();
}
} catch (SQLException ex) {
log.log(Level.WARNING,
"Problem adding/updating an item to DB: " + item.toElement() + "\n", ex);
// log.log(Level.WARNING,
// "SQLWarning: " + updateItemSt.getWarnings().getMessage() +
// ", state: " + updateItemSt.getWarnings().getSQLState());
}
} catch (Exception e) {
log.warning(e + "Exception while adding config item: " + item.toString());
}
}
private void checkDB() throws SQLException {
ResultSet rs = null;
Statement st = null;
try {
if ( !data_repo.checkTable(TABLE_NAME)) {
st = data_repo.createStatement(null);
st.executeUpdate(CREATE_TABLE_QUERY);
} else {
log.info("DB for server configuration OK.");
}
} finally {
data_repo.release(st, rs);
rs = null;
st = null;
}
}
private ConfigItem createItemFromRS(ResultSet rs) throws SQLException {
ConfigItem result = getItemInstance();
String clusterNode = rs.getString(CLUSTER_NODE_COLUMN);
String compName = rs.getString(COMPONENT_NAME_COLUMN);
String nodeName = rs.getString(NODE_NAME_COLUMN);
String keyName = rs.getString(KEY_NAME_COLUMN);
String value_str = rs.getString(VALUE_COLUMN);
String value_type = rs.getString(VALUE_TYPE_COLUMN);
String flag_str = rs.getString(FLAG_COLUMN);
result.set(clusterNode, compName, nodeName, keyName, value_str, value_type.charAt(0),
flag_str);
return result;
}
//~--- get methods --------------------------------------------------------
private Collection<ConfigItem> getAllItems() {
List<ConfigItem> result = new ArrayList<ConfigItem>();
ResultSet rs = null;
try {
PreparedStatement getAllItemsSt = data_repo.getPreparedStatement(null, GET_ALL_ITEMS_QUERY);
synchronized (getAllItemsSt) {
getAllItemsSt.setString(1, getDefHostname());
rs = getAllItemsSt.executeQuery();
while (rs.next()) {
ConfigItem item = createItemFromRS(rs);
if (item.getFlag() != ConfigItem.FLAGS.INITIAL) {
result.add(item);
}
}
}
} catch (SQLException e) {
log.log(Level.WARNING, "Problem getting elements from DB: ", e);
} finally {
data_repo.release(null, rs);
}
return result;
}
private Set<ConfigItem> getCompItems(String compName) {
Set<ConfigItem> result = new LinkedHashSet<ConfigItem>();
ResultSet rs = null;
try {
PreparedStatement getCompItemsSt =
data_repo.getPreparedStatement(null, GET_COMPONENT_ITEMS_QUERY);
synchronized (getCompItemsSt) {
getCompItemsSt.setString(1, getDefHostname());
getCompItemsSt.setString(2, compName);
rs = getCompItemsSt.executeQuery();
while (rs.next()) {
ConfigItem item = createItemFromRS(rs);
if (item.getFlag() != ConfigItem.FLAGS.INITIAL) {
result.add(item);
}
}
}
} catch (SQLException e) {
log.log(Level.WARNING, "Problem getting elements from DB: ", e);
} finally {
data_repo.release(null, rs);
}
return result;
}
private String[] getComponentNames() {
List<String> result = new ArrayList<String>();
ResultSet rs = null;
try {
PreparedStatement getCompNamesSt =
data_repo.getPreparedStatement(null, GET_COMPONENT_NAMES_QUERY);
synchronized (getCompNamesSt) {
getCompNamesSt.setString(1, getDefHostname());
rs = getCompNamesSt.executeQuery();
while (rs.next()) {
result.add(rs.getString(COMPONENT_NAME_COLUMN));
}
}
} catch (SQLException e) {
log.log(Level.WARNING, "Problem getting component names from DB: ", e);
} finally {
data_repo.release(null, rs);
}
return result.toArray(new String[result.size()]);
}
private ConfigItem getItem(String compName, String node, String key) {
ConfigItem result = null;
ResultSet rs = null;
try {
PreparedStatement getItemSt = data_repo.getPreparedStatement(null, GET_ITEM_QUERY);
synchronized (getItemSt) {
getItemSt.setString(1, getDefHostname());
getItemSt.setString(2, compName);
getItemSt.setString(3, node);
getItemSt.setString(4, key);
rs = getItemSt.executeQuery();
while (rs.next()) {
ConfigItem item = createItemFromRS(rs);
if (item.getFlag() != ConfigItem.FLAGS.INITIAL) {
result = item;
break;
}
}
}
} catch (SQLException e) {
log.log(Level.WARNING, "Problem getting elements from DB: ", e);
} finally {
data_repo.release(null, rs);
}
return result;
}
private String[] getKeys(String compName, String node) {
List<String> result = new ArrayList<String>();
ResultSet rs = null;
try {
PreparedStatement getKeysSt = data_repo.getPreparedStatement(null, GET_KEYS_QUERY);
synchronized (getKeysSt) {
getKeysSt.setString(1, getDefHostname());
getKeysSt.setString(2, compName);
getKeysSt.setString(3, node);
rs = getKeysSt.executeQuery();
while (rs.next()) {
result.add(rs.getString(KEY_NAME_COLUMN));
}
}
} catch (SQLException e) {
log.log(Level.WARNING, "Problem getting keys from DB: ", e);
} finally {
data_repo.release(null, rs);
}
return result.toArray(new String[result.size()]);
}
private int getPropertiesCount() {
int result = 0;
ResultSet rs = null;
try {
PreparedStatement getPropertiesCountSt =
data_repo.getPreparedStatement(null, GET_PROPERTIES_COUNT_QUERY);
synchronized (getPropertiesCountSt) {
getPropertiesCountSt.setString(1, getDefHostname());
rs = getPropertiesCountSt.executeQuery();
while (rs.next()) {
result = rs.getInt("count");
}
}
} catch (SQLException e) {
log.log(Level.WARNING, "Problem getting elements count from DB: ", e);
} finally {
data_repo.release(null, rs);
}
return result;
}
//~--- methods ------------------------------------------------------------
private void removeItem(ConfigItem item) {
try {
PreparedStatement deleteItemSt = data_repo.getPreparedStatement(null, DELETE_ITEM_QUERY);
synchronized (deleteItemSt) {
deleteItemSt.setString(1, ((item.getClusterNode() != null) ? item.getClusterNode() : ""));
deleteItemSt.setString(2, item.getCompName());
deleteItemSt.setString(3, ((item.getNodeName() != null) ? item.getNodeName() : ""));
deleteItemSt.setString(4, item.getKeyName());
deleteItemSt.executeUpdate();
}
} catch (SQLException e) {
log.log(Level.WARNING, "Problem removing an item from DB: " + item.toElement(), e);
}
}
}
}
//~ Formatted in Sun Code Convention
//~ Formatted by Jindent --- http://www.jindent.com