package org.ff4j.property.store;
/*
* #%L
* ff4j-core
* %%
* Copyright (C) 2013 - 2015 Ff4J
* %%
* 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.
* #L%
*/
import static org.ff4j.store.JdbcStoreConstants.COL_PROPERTY_ID;
import static org.ff4j.utils.JdbcUtils.buildStatement;
import static org.ff4j.utils.JdbcUtils.closeConnection;
import static org.ff4j.utils.JdbcUtils.closeResultSet;
import static org.ff4j.utils.JdbcUtils.closeStatement;
import static org.ff4j.utils.JdbcUtils.executeUpdate;
import static org.ff4j.utils.JdbcUtils.isTableExist;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import org.ff4j.exception.PropertyAccessException;
import org.ff4j.exception.PropertyAlreadyExistException;
import org.ff4j.exception.PropertyNotFoundException;
import org.ff4j.property.Property;
import org.ff4j.store.JdbcQueryBuilder;
import org.ff4j.utils.Util;
/**
* Access information related to properties within database.
*
* @author Cedrick Lunven (@clunven)
*/
public class JdbcPropertyStore extends AbstractPropertyStore {
/** Access to storage. */
private DataSource dataSource;
/** Query builder. */
private JdbcQueryBuilder queryBuilder;
/** Mapper. */
private JdbcPropertyMapper JDBC_MAPPER = new JdbcPropertyMapper();
/** Default Constructor. */
public JdbcPropertyStore() {}
/**
* Constructor from DataSource.
*
* @param jdbcDS
* native jdbc datasource
*/
public JdbcPropertyStore(DataSource jdbcDS) {
this.dataSource = jdbcDS;
}
/**s
* Constructor from DataSource.
*
* @param jdbcDS
* native jdbc datasource
*/
public JdbcPropertyStore(DataSource jdbcDS, String xmlConfFile) {
this(jdbcDS);
importPropertiesFromXmlFile(xmlConfFile);
}
/** {@inheritDoc} */
@Override
public void createSchema() {
DataSource ds = getDataSource();
JdbcQueryBuilder qb = getQueryBuilder();
if (!isTableExist(ds, qb.getTableNameProperties())) {
executeUpdate(ds, qb.sqlCreateTableProperties());
}
}
/** {@inheritDoc} */
public boolean existProperty(String name) {
Util.assertHasLength(name);
PreparedStatement ps = null;
ResultSet rs = null;
Connection sqlConn = null;
try {
sqlConn = getDataSource().getConnection();
ps = buildStatement(sqlConn, getQueryBuilder().existProperty(), name);
rs = ps.executeQuery();
rs.next();
return 1 == rs.getInt(1);
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot check feature existence, error related to database", sqlEX);
} finally {
closeResultSet(rs);
closeStatement(ps);
closeConnection(sqlConn);
}
}
/** {@inheritDoc} */
public <T> void createProperty(Property<T> ap) {
Util.assertNotNull(ap);
Connection sqlConn = null;
PreparedStatement ps = null;
try {
sqlConn = getDataSource().getConnection();
if (existProperty(ap.getName())) {
throw new PropertyAlreadyExistException(ap.getName());
}
ps = sqlConn.prepareStatement(getQueryBuilder().createProperty());
ps.setString(1, ap.getName());
ps.setString(2, ap.getType());
ps.setString(3, ap.asString());
ps.setString(4, ap.getDescription());
if (ap.getFixedValues() != null && !ap.getFixedValues().isEmpty()) {
String fixedValues = ap.getFixedValues().toString();
ps.setString(5, fixedValues.substring(1, fixedValues.length() - 1));
} else {
ps.setString(5, null);
}
ps.executeUpdate();
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot update properties database, SQL ERROR", sqlEX);
} finally {
// Connection is closed alse here within clos statement
closeStatement(ps);
closeConnection(sqlConn);
}
}
/** {@inheritDoc} */
public Property<?> readProperty(String name) {
Util.assertHasLength(name);
Connection sqlConn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
sqlConn = getDataSource().getConnection();
if (!existProperty(name)) {
throw new PropertyNotFoundException(name);
}
// Returns features
ps = buildStatement(sqlConn, getQueryBuilder().getProperty(), name);
rs = ps.executeQuery();
rs.next();
return JDBC_MAPPER.map(rs);
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot check property existence, error related to database", sqlEX);
} finally {
closeResultSet(rs);
closeStatement(ps);
closeConnection(sqlConn);
}
}
/** {@inheritDoc} */
public void updateProperty(String name, String newValue) {
Util.assertHasLength(name);
Connection sqlConn = null;
PreparedStatement ps = null;
try {
sqlConn = getDataSource().getConnection();
// Check existence
Property<?> ab = readProperty(name);
// Check new value validity
ab.fromString(newValue);
ps = buildStatement(sqlConn, getQueryBuilder().updateProperty(), newValue, name);
ps.executeUpdate();
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot update property database, SQL ERROR", sqlEX);
} finally {
closeStatement(ps);
closeConnection(sqlConn);
}
}
/** {@inheritDoc} */
public <T> void updateProperty(Property<T> prop) {
if (prop == null || prop.getName() == null) {
throw new IllegalArgumentException("Cannot update property, please provide property name");
}
deleteProperty(prop.getName());
createProperty(prop);
}
/** {@inheritDoc} */
public void deleteProperty(String name) {
Util.assertHasLength(name);
Connection sqlConn = null;
PreparedStatement ps = null;
try {
sqlConn = getDataSource().getConnection();
if (!existProperty(name)) {
throw new PropertyNotFoundException(name);
}
ps = buildStatement(sqlConn, getQueryBuilder().deleteProperty(), name);
ps.executeUpdate();
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot delete property database, SQL ERROR", sqlEX);
} finally {
closeStatement(ps);
closeConnection(sqlConn);
}
}
/** {@inheritDoc} */
@Override
public Map<String, Property<?>> readAllProperties() {
Map<String, Property<?>> properties = new LinkedHashMap<String, Property<?>>();
Connection sqlConn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
sqlConn = getDataSource().getConnection();
ps = buildStatement(sqlConn, getQueryBuilder().getAllProperties());
rs = ps.executeQuery();
while (rs.next()) {
Property<?> ap = JDBC_MAPPER.map(rs);
properties.put(ap.getName(),ap);
}
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot read properties within database, SQL ERROR", sqlEX);
} finally {
closeResultSet(rs);
closeStatement(ps);
closeConnection(sqlConn);
}
return properties;
}
/** {@inheritDoc} */
public Set<String> listPropertyNames() {
Set < String > propertyNames = new HashSet<String>();
PreparedStatement ps = null;
Connection sqlConn = null;
ResultSet rs = null;
try {
sqlConn = getDataSource().getConnection();
ps = buildStatement(sqlConn, getQueryBuilder().getAllPropertiesNames());
rs = ps.executeQuery();
while (rs.next()) {
propertyNames.add(rs.getString(COL_PROPERTY_ID));
}
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot read properties within database, SQL ERROR", sqlEX);
} finally {
closeResultSet(rs);
closeStatement(ps);
closeConnection(sqlConn);
}
return propertyNames;
}
/** {@inheritDoc} */
public void clear() {
PreparedStatement ps = null;
Connection sqlConn = null;
try {
sqlConn = getDataSource().getConnection();
ps = buildStatement(sqlConn, getQueryBuilder().deleteAllProperties());
ps.executeUpdate();
} catch (SQLException sqlEX) {
throw new PropertyAccessException("Cannot clear properties table, SQL ERROR", sqlEX);
} finally {
closeStatement(ps);
closeConnection(sqlConn);
}
}
/**
* Getter accessor for attribute 'dataSource'.
*
* @return
* current value of 'dataSource'
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* Setter accessor for attribute 'dataSource'.
* @param dataSource
* new value for 'dataSource '
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* @return the queryBuilder
*/
public JdbcQueryBuilder getQueryBuilder() {
if (queryBuilder == null) {
queryBuilder = new JdbcQueryBuilder();
}
return queryBuilder;
}
/**
* @param queryBuilder the queryBuilder to set
*/
public void setQueryBuilder(JdbcQueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
}
}