package org.ff4j.property.store;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.ff4j.conf.XmlConfig;
import org.ff4j.conf.XmlParser;
import org.ff4j.exception.PropertyAlreadyExistException;
import org.ff4j.exception.PropertyNotFoundException;
import org.ff4j.property.Property;
import org.ff4j.utils.Util;
/*
* #%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%
*/
/**
* Superclass for any property store.
*
* @author Cedrick Lunven (@clunven)
*/
public abstract class AbstractPropertyStore implements PropertyStore {
/**
* Initialize store from XML Configuration File.
*
* @param xmlConfFile
* xml configuration file
*/
public Map<String, Property<?>> importPropertiesFromXmlFile(String xmlConfFile) {
// Argument validation
if (xmlConfFile == null || xmlConfFile.isEmpty()) {
throw new IllegalArgumentException("Configuration filename cannot be null nor empty");
}
// Load as Inputstream
InputStream xmlIS = getClass().getClassLoader().getResourceAsStream(xmlConfFile);
if (xmlIS == null) {
throw new IllegalArgumentException("File " + xmlConfFile + " could not be read, please check path and rights");
}
// Use the Feature Parser
XmlConfig conf = new XmlParser().parseConfigurationFile(xmlIS);
Map<String, Property<?>> properties = conf.getProperties();
// Override existing configuration within database
for (Map.Entry<String,Property<?>> featureName : properties.entrySet()) {
if (existProperty(featureName.getKey())) {
deleteProperty(featureName.getKey());
}
createProperty(featureName.getValue());
}
return properties;
}
/** {@inheritDoc} */
@Override
public boolean isEmpty() {
Set < String > pNames = listPropertyNames();
return pNames == null || pNames.isEmpty();
}
/** {@inheritDoc} */
public String toJson() {
StringBuilder sb = new StringBuilder("{");
sb.append("\"type\":\"" + this.getClass().getCanonicalName() + "\"");
Set<String> myProperties = readAllProperties().keySet();
sb.append(",\"numberOfProperties\":" + myProperties.size());
sb.append(",\"properties\":[");
boolean first = true;
for (String myProperty : myProperties) {
if (!first) {
sb.append(",");
}
first = false;
sb.append("\"" + myProperty + "\"");
}
sb.append("]}");
return sb.toString();
}
/**
* Validate property name and existence
*
* @param uid
* target uid
*/
protected void assertPropertyExist(String name) {
Util.assertHasLength(name);
if (!existProperty(name)) {
throw new PropertyNotFoundException(name);
}
}
/**
* Check that current feature does not exist.
*
* @param uid
* current feature identifier.s
*/
protected void assertPropertyNotExist(String uid) {
Util.assertHasLength(uid);
if (existProperty(uid)) {
throw new PropertyAlreadyExistException(uid);
}
}
/**
* Validate feature uid.
*
* @param uid
* target uid
*/
protected void assertPropertyNotNull(Property<?> property) {
if (property == null) {
throw new IllegalArgumentException("Property cannot be null nor empty");
}
}
/** {@inheritDoc} */
@Override
public <T> void updateProperty(Property<T> prop) {
Util.assertNotNull(prop);
// Delete
deleteProperty(prop.getName());
// Create
createProperty(prop);
}
/** {@inheritDoc} */
@Override
public void updateProperty(String name, String newValue) {
// Read from redis, feature not found if no present
Property<?> p = readProperty(name);
// Update within Object
p.setValueFromString(newValue);
// Serialization and update key, update TTL
updateProperty(p);
}
/** {@inheritDoc} */
@Override
public void importProperties(Collection<Property<?>> properties) {
// Do not use target as the delete/create operation will be traced
if (properties != null) {
for (Property<?> property : properties) {
if (existProperty(property.getName())) {
deleteProperty(property.getName());
}
createProperty(property);
}
}
}
/** {@inheritDoc} */
@Override
public Property<?> readProperty(String name, Property < ? > defaultValue) {
try {
return readProperty(name);
} catch(PropertyNotFoundException pnf) {
return defaultValue;
}
}
/** {@inheritDoc} */
@Override
public void createSchema() {
/*
* In most of cases there is nothing to do. The feature and properties are createdat runtime.
* But not always (JDBC, Mongo, Cassandra)... this is the reason why the dedicated store must
* override this method. It a default implementation (Pattern Adapter).
*/
return;
}
}