/*
* Copyright 2008-2014 the original author or authors
*
* 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.
*/
package org.kaleidofoundry.core.config;
import static org.kaleidofoundry.core.config.ConfigurationConstants.KeyRoot;
import static org.kaleidofoundry.core.config.ConfigurationConstants.KeySeparator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.kaleidofoundry.core.cache.Cache;
import org.kaleidofoundry.core.context.RuntimeContext;
import org.kaleidofoundry.core.plugin.Declare;
import org.kaleidofoundry.core.store.ResourceException;
import org.kaleidofoundry.core.store.ResourceHandler;
import org.kaleidofoundry.core.util.StringHelper;
/**
* Json configuration implementation
*
* @author jraduget
*/
@Declare(ConfigurationConstants.JsonConfigurationPluginName)
public class JsonConfiguration extends AbstractConfiguration {
private static final JsonFactory JSON_FACTORY = new JsonFactory();
JsonConfiguration() {
super();
}
/**
* @param context
* @throws ResourceException
*/
public JsonConfiguration(final RuntimeContext<Configuration> context) throws ResourceException {
super(context);
}
/**
* @param name
* @param resourceUri
* @param context
* @throws ResourceException
*/
public JsonConfiguration(final String name, final String resourceUri, final RuntimeContext<Configuration> context) throws ResourceException {
super(name, resourceUri, context);
}
@Override
protected Cache<String, Serializable> loadProperties(final ResourceHandler resourceHandler, final Cache<String, Serializable> properties)
throws ResourceException, ConfigurationException {
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode rootNode = mapper.readValue(resourceHandler.getReader(), JsonNode.class);
if (rootNode != null) {
feedProperties(rootNode.getFields(), new StringBuilder(), properties);
}
return properties;
} catch (JsonParseException jpe) {
throw new ConfigurationException("config.load.json.dom.error", jpe);
} catch (JsonMappingException jme) {
throw new ConfigurationException("config.load.json.parsing.error", jme, singleFileStore.getResourceBinding().toString());
} catch (IOException ioe) {
throw new ResourceException(ioe, resourceHandler.getUri());
}
}
/*
* (non-Javadoc)
* @see org.kaleidofoundry.core.config.AbstractConfiguration#storeProperties(org.kaleidofoundry.core.store.ResourceHandler,
* org.kaleidofoundry.core.cache.Cache)
*/
@Override
protected Cache<String, Serializable> storeProperties(final ResourceHandler resourceHandler, final Cache<String, Serializable> properties)
throws ResourceException, ConfigurationException {
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();
Map<String, ObjectNode> currentNodes = new HashMap<String, ObjectNode>();
for (String key : keySet()) {
StringTokenizer strToken = new StringTokenizer(key.substring(KeyRoot.length()), KeySeparator);
String subKey = "";
ObjectNode node = rootNode;
while (strToken.hasMoreTokens()) {
String nodeName = strToken.nextToken();
subKey = subKey + KeySeparator + nodeName;
ObjectNode newNode;
if (!currentNodes.containsKey(subKey)) {
newNode = mapper.createObjectNode();
currentNodes.put(subKey, newNode);
} else {
newNode = currentNodes.get(subKey);
}
if (strToken.hasMoreElements()) {
node.put(nodeName, newNode);
node = newNode;
} else {
List<String> values = getStringList(key);
if (values == null || values.size() == 1) {
node.put(nodeName, getString(key));
} else {
ArrayNode arrayNode = mapper.createArrayNode();
for (String value : values) {
arrayNode.add(value);
}
node.put(nodeName, arrayNode);
}
node = newNode;
}
}
}
ByteArrayOutputStream jsonOutput = null;
JsonGenerator jsonGenerator = null;
try {
// create output stream and json serializer
jsonOutput = new ByteArrayOutputStream();
jsonGenerator = JSON_FACTORY.createJsonGenerator(jsonOutput);
jsonGenerator.useDefaultPrettyPrinter();
// jsonGenerator.setPrettyPrinter(new DefaultPrettyPrinter());
// write json content
mapper.writeTree(jsonGenerator, rootNode);
// flush output stream writes
jsonGenerator.close();
// Store the document content
singleFileStore.store(singleFileStore.createResourceHandler(resourceHandler.getUri(), new ByteArrayInputStream(jsonOutput.toByteArray())));
return properties;
} catch (IOException ioe) {
if (ioe instanceof ResourceException) {
throw (ResourceException) ioe;
} else {
throw new ResourceException(ioe, resourceHandler.getUri());
}
} finally {
if (jsonGenerator != null && !jsonGenerator.isClosed()) {
try {
jsonGenerator.close();
} catch (IOException ioe) {
throw new ResourceException(ioe, resourceHandler.getUri());
}
}
}
}
/**
* recursive method, use to
*
* @param nodeList
* @param keyName
* @param properties
*/
protected void feedProperties(final Iterator<Entry<String, JsonNode>> nodesByName, final StringBuilder keyName, final Cache<String, Serializable> properties) {
while (nodesByName.hasNext()) {
Entry<String, JsonNode> node = nodesByName.next();
final StringBuilder newKeyName = new StringBuilder(keyName).append(keyName.length() > 0 ? KeySeparator : KeyRoot).append(node.getKey());
if (node.getValue().isContainerNode()) {
if (node.getValue().isArray()) {
List<String> result = new LinkedList<String>();
Iterator<JsonNode> arrayValues = node.getValue().iterator();
while (arrayValues.hasNext()) {
result.add(arrayValues.next().asText());
}
properties.put(newKeyName.toString(), StringHelper.unsplit(MultiValuesSeparator, result.toArray(new Object[result.size()])));
} else {
feedProperties(node.getValue().getFields(), newKeyName, properties);
}
} else {
properties.put(newKeyName.toString(), node.getValue().getTextValue());
}
}
}
}