/* * 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.KeyPropertiesSeparator; import static org.kaleidofoundry.core.config.ConfigurationConstants.KeyRoot; import static org.kaleidofoundry.core.config.ConfigurationConstants.KeySeparator; import static org.kaleidofoundry.core.config.ConfigurationConstants.XmlRootElement; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; 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; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Xml configuration implementation :<br/> * * <pre> * <root> * <application> * <name>app</name> * <version>1.0.0</version> * <description>description of the application...</description> * <date>2006-09-01</date> * <librairies> * <value>dom4j.jar</value> * <value>log4j.jar</value> * <value>mail.jar</value> * </librairies> * <modules> * <sales>Sales<version>1.1.0</version></sales> * <marketing>Market.</marketing> * <netbusiness></netbusiness> * </modules> * </application> * </root> * </pre> * * @author jraduget */ @Declare(ConfigurationConstants.XmlConfigurationPluginName) public class XmlConfiguration extends AbstractConfiguration implements Configuration { /** * @param context * @throws ResourceException */ public XmlConfiguration(final RuntimeContext<Configuration> context) throws ResourceException { super(context); } /** * @param name * @param resourceUri * @param context * @throws ResourceException */ public XmlConfiguration(final String name, final String resourceUri, final RuntimeContext<Configuration> context) throws ResourceException { super(name, resourceUri, context); } /** * @see AbstractConfiguration#AbstractConfiguration() */ XmlConfiguration() { super(); } /* * (non-Javadoc) * @see org.kaleidofoundry.core.config.AbstractConfiguration#loadProperties(org.kaleidofoundry.core.store.ResourceHandler, * org.kaleidofoundry.core.cache.Cache) */ @Override protected Cache<String, Serializable> loadProperties(final ResourceHandler resourceHandler, final Cache<String, Serializable> cacheProperties) throws ResourceException, ConfigurationException { try { final DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); final DocumentBuilder documentBuilder = domFactory.newDocumentBuilder(); final Document document = documentBuilder.parse(new InputSource(resourceHandler.getReader())); final Element root = document.getDocumentElement(); if (root != null) { feedProperties(root.getChildNodes(), new StringBuilder(), cacheProperties); } return cacheProperties; } catch (final ParserConfigurationException pce) { throw new ConfigurationException("config.load.xml.dom.error", pce); } catch (final SAXException sae) { throw new ConfigurationException("config.load.xml.parsing.error", sae, singleFileStore.getResourceBinding().toString()); } catch (final 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> cacheProperties) throws ResourceException, ConfigurationException { try { final DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); final DocumentBuilder documentBuilder = domFactory.newDocumentBuilder(); final Document document = documentBuilder.newDocument(); final Element root = document.createElement(XmlRootElement); Map<String, Element> currentNodes = new HashMap<String, Element>(); document.setXmlStandalone(true); document.appendChild(root); for (String key : keySet()) { StringTokenizer strToken = new StringTokenizer(key.substring(KeyRoot.length()), KeySeparator); String subKey = KeyRoot; Element element = root; while (strToken.hasMoreTokens()) { String nodeName = strToken.nextToken(); subKey = subKey + KeySeparator + nodeName; Element newElement = currentNodes.containsKey(subKey) ? currentNodes.get(subKey) : document.createElement(nodeName); currentNodes.put(subKey, newElement); element.appendChild(newElement); element = newElement; if (!strToken.hasMoreElements()) { List<String> values = getStringList(key); if (values == null || values.size() == 1) { newElement.setTextContent(getString(key)); } else { for (String value : values) { Element valueElt = document.createElement("value"); valueElt.setTextContent(value); newElement.appendChild(valueElt); } } } } } Source source = new DOMSource(document); ByteArrayOutputStream out = new ByteArrayOutputStream(); Result result = new StreamResult(out); // Write the DOM document to the file Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform(source, result); // Store the document content singleFileStore.store(singleFileStore.createResourceHandler(resourceHandler.getUri(), new ByteArrayInputStream(out.toByteArray()))); return cacheProperties; } catch (final ParserConfigurationException pce) { throw new ConfigurationException("config.store.xml.error", pce); } catch (TransformerConfigurationException e) { throw new ConfigurationException("config.store.xml.error", e); } catch (TransformerFactoryConfigurationError e) { throw new ConfigurationException("config.store.xml.error", e); } catch (TransformerException e) { throw new ConfigurationException("config.store.xml.error", e); } } /** * recursive method, use to * * @param nodeList * @param keyName * @param properties */ protected void feedProperties(final NodeList nodeList, final StringBuilder keyName, final Cache<String, Serializable> properties) { for (int i = 0; i < nodeList.getLength(); i++) { final Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { final StringBuilder newKeyName = new StringBuilder(keyName).append(keyName.length() > 0 ? KeyPropertiesSeparator : "").append(node.getNodeName()); // node without element child, but which containing test if (node.hasChildNodes() && node.getChildNodes().getLength() == 1 && node.getFirstChild().getNodeType() == Node.TEXT_NODE) { // LOGGER.debug("found key={} , value={}", newKeyName.toString(), node.getTextContent()); properties.put(normalizeKey(newKeyName.toString()), node.getTextContent()); } // node without element child, but which containing test else if (!node.hasChildNodes()) { // LOGGER.debug("found key={} , value={}", newKeyName.toString(), ""); properties.put(normalizeKey(newKeyName.toString()), ""); } else { final List<String> multipleValues = nodeMultipleValues(node.getChildNodes()); // simple element, recursive call, on children nodes if (multipleValues == null) { feedProperties(node.getChildNodes(), newKeyName, properties); } // multiple value have been detected else { properties.put(normalizeKey(newKeyName.toString()), StringHelper.unsplit(MultiValuesSeparator, multipleValues.toArray(new Object[multipleValues.size()]))); // properties.setMultiValueProperty(newKeyName.toString(), multipleValues.toArray(new String[multipleValues.size()])); } } } } } /** * @param nodeList * @return node multiple values */ protected List<String> nodeMultipleValues(final NodeList nodeList) { List<String> result = null; for (int i = 0; i < nodeList.getLength(); i++) { final Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE && "value".equals(node.getNodeName())) { if (result == null) { result = new ArrayList<String>(); } if (node.hasChildNodes() && node.getChildNodes().getLength() == 1 && node.getFirstChild().getNodeType() == Node.TEXT_NODE) { result.add(node.getTextContent()); } } } return result; } }