/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.sling.installer.provider.jcr.impl; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Array; import java.math.BigInteger; import java.security.MessageDigest; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import java.util.SortedSet; import java.util.TreeSet; import javax.jcr.Node; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Value; import org.apache.sling.installer.api.InstallableResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Converts configuration nodes to InstallableData */ class ConfigNodeConverter implements JcrInstaller.NodeConverter { public static final String CONFIG_NODE_TYPE = "sling:OsgiConfig"; private final Logger log = LoggerFactory.getLogger(getClass()); /** Convert n to an InstallableData, or return null * if we don't know how to convert it. */ public InstallableResource convertNode(final Node n, final int priority) throws RepositoryException { InstallableResource result = null; // We only consider CONFIG_NODE_TYPE nodes if(n.isNodeType(CONFIG_NODE_TYPE)) { final Dictionary<String, Object> dict = load(n); result = new InstallableResource(n.getPath(), null, dict, computeDigest(dict), null, priority); log.debug("Converted node {} to {}", n.getPath(), result); } else { log.debug("Node is not a {} node, ignored:{}", CONFIG_NODE_TYPE, n.getPath()); } return result; } /** Load config from node n */ protected Dictionary<String, Object> load(Node n) throws RepositoryException { Dictionary<String, Object> result = new Hashtable<String, Object>(); log.debug("Loading config from Node {}", n.getPath()); // load default values from node itself log.debug("Loading {} properties", n.getPath()); loadProperties(result, n); return result; } /** Load properties of n into d */ protected void loadProperties(Dictionary<String, Object> d, Node n) throws RepositoryException { final PropertyIterator pi = n.getProperties(); while(pi.hasNext()) { final Property p = pi.nextProperty(); final String name = p.getName(); // ignore jcr: and similar properties if(name.contains(":")) { continue; } if (p.getDefinition().isMultiple()) { Object [] data = null; final Value [] values = p.getValues(); int i = 0; for (Value v : values) { Object o = convertValue(v); if (i == 0) { data = (Object[])Array.newInstance(o.getClass(), values.length); } data[i++] = o; } // create empty array in case no value is specified if ( data == null ) { data = new String[0]; } d.put(name, data); } else { final Object o = convertValue(p.getValue()); if (o != null) { d.put(name, o); } } } } /** * Convert v according to its type. */ protected Object convertValue(Value v) throws RepositoryException { switch(v.getType()) { case PropertyType.STRING: return v.getString(); case PropertyType.DATE: return v.getDate(); case PropertyType.DOUBLE: return v.getDouble(); case PropertyType.LONG: return v.getLong(); case PropertyType.BOOLEAN: return v.getBoolean(); } log.debug("Value of type {} ignored", v.getType()); return null; } /** convert digest to readable string (http://www.javalobby.org/java/forums/t84420.html) */ private static String digestToString(MessageDigest d) { final BigInteger bigInt = new BigInteger(1, d.digest()); return new String(bigInt.toString(16)); } /** Digest is needed to detect changes in data, and must not depend on dictionary ordering */ private static String computeDigest(Dictionary<String, Object> data) { try { final MessageDigest d = MessageDigest.getInstance("MD5"); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final ObjectOutputStream oos = new ObjectOutputStream(bos); final SortedSet<String> sortedKeys = new TreeSet<String>(); if(data != null) { for(Enumeration<String> e = data.keys(); e.hasMoreElements(); ) { final String key = e.nextElement(); sortedKeys.add(key); } } for(String key : sortedKeys) { oos.writeObject(key); oos.writeObject(data.get(key)); } bos.flush(); d.update(bos.toByteArray()); return digestToString(d); } catch (Exception ignore) { return data.toString(); } } }