Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id F308A200B2B for ; Tue, 28 Jun 2016 15:14:42 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id F1B7C160A6C; Tue, 28 Jun 2016 13:14:42 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id D0B8E160A56 for ; Tue, 28 Jun 2016 15:14:40 +0200 (CEST) Received: (qmail 41347 invoked by uid 500); 28 Jun 2016 13:14:39 -0000 Mailing-List: contact commits-help@felix.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@felix.apache.org Delivered-To: mailing list commits@felix.apache.org Received: (qmail 40831 invoked by uid 99); 28 Jun 2016 13:14:37 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd3-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 28 Jun 2016 13:14:37 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd3-us-west.apache.org (ASF Mail Server at spamd3-us-west.apache.org) with ESMTP id 719941877FE for ; Tue, 28 Jun 2016 13:14:36 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd3-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 0.374 X-Spam-Level: X-Spam-Status: No, score=0.374 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-1.426] autolearn=disabled Received: from mx1-lw-us.apache.org ([10.40.0.8]) by localhost (spamd3-us-west.apache.org [10.40.0.10]) (amavisd-new, port 10024) with ESMTP id XLgQlZJ5djjY for ; Tue, 28 Jun 2016 13:14:25 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-us.apache.org (ASF Mail Server at mx1-lw-us.apache.org) with ESMTP id D4CC85FD3B for ; Tue, 28 Jun 2016 13:14:24 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 8CA44E08E4 for ; Tue, 28 Jun 2016 13:14:23 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 16C8E3A0A1D for ; Tue, 28 Jun 2016 13:14:22 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1750501 [2/2] - in /felix/sandbox/cziegeler/configurator: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/configurator/ src/main/java/org/apache/f... Date: Tue, 28 Jun 2016 13:14:21 -0000 To: commits@felix.apache.org From: cziegeler@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20160628131422.16C8E3A0A1D@svn01-us-west.apache.org> archived-at: Tue, 28 Jun 2016 13:14:43 -0000 Added: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigPolicy.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigPolicy.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigPolicy.java (added) +++ felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigPolicy.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,25 @@ +/* + * 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.felix.configurator.impl.model; + +public enum ConfigPolicy { + + DEFAULT, + FORCE +} Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigPolicy.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigPolicy.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigState.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigState.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigState.java (added) +++ felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigState.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,39 @@ +/* + * 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.felix.configurator.impl.model; + +/** + * The state of a configuration. + * + * The state represents the configurator's view. It might not + * reflect the current state of the system. For example if a + * configuration is installed through the configurator, it gets + * the state "INSTALLED". However if an administrator now deletes + * the configuration through any other way like e.g. the web console, + * the configuration still has the state "INSTALLED". + * + */ +public enum ConfigState { + + INSTALL, // the configuration should be installed + UNINSTALL, // the configuration should be uninstalled + INSTALLED, // the configuration is installed + UNINSTALLED, // the configuration is uninstalled + IGNORED // the configuration is ignored +} Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigState.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigState.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigurationFile.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigurationFile.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigurationFile.java (added) +++ felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigurationFile.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,52 @@ +/* + * 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.felix.configurator.impl.model; + +import java.net.URL; +import java.util.List; + +/** + * This object holds all configurations from a single file. + * This is only an intermediate object. + */ +public class ConfigurationFile implements Comparable { + + private final URL url; + + private final List configurations; + + public ConfigurationFile(final URL url, final List configs) { + this.url = url; + this.configurations = configs; + } + + @Override + public int compareTo(final ConfigurationFile o) { + return url.getPath().compareTo(o.url.getPath()); + } + + @Override + public String toString() { + return "ConfigurationFile [url=" + url + ", configurations=" + configurations + "]"; + } + + public List getConfigurations() { + return this.configurations; + } +} Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigurationFile.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/ConfigurationFile.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java (added) +++ felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,189 @@ +/* + * 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.felix.configurator.impl.model; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.felix.configurator.impl.Util; +import org.apache.felix.configurator.impl.logger.SystemLogger; +import org.osgi.framework.BundleContext; + +public class State extends AbstractState implements Serializable { + + private static final long serialVersionUID = 1L; + + /** Serialization version. */ + private static final int VERSION = 1; + + private static final String FILE_NAME = "state.ser"; + + private final Map bundlesLastModified = new HashMap(); + + private final Set environments = new HashSet<>(); + + private volatile Set initialHashes; + + volatile transient boolean envsChanged = true; + + /** + * Serialize the object + * - write version id + * - serialize fields + * @param out Object output stream + * @throws IOException + */ + private void writeObject(final java.io.ObjectOutputStream out) + throws IOException { + out.writeInt(VERSION); + out.writeObject(bundlesLastModified); + out.writeObject(environments); + out.writeObject(initialHashes); + } + + /** + * Deserialize the object + * - read version id + * - deserialize fields + */ + @SuppressWarnings("unchecked") + private void readObject(final java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + final int version = in.readInt(); + if ( version < 1 || version > VERSION ) { + throw new ClassNotFoundException(this.getClass().getName()); + } + Util.setField(this, "bundlesLastModified", in.readObject()); + Util.setField(this, "environments", in.readObject()); + initialHashes = (Set) in.readObject(); + } + + public static State createOrReadState(final BundleContext bc) { + final File f = bc.getDataFile(FILE_NAME); + if ( f == null || !f.exists() ) { + return new State(); + } + try ( final ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f)) ) { + + return (State) ois.readObject(); + } catch ( final ClassNotFoundException | IOException e ) { + SystemLogger.error("Unable to read persisted state from " + f, e); + return new State(); + } + } + + public static void writeState(final BundleContext bc, final State state) { + final File f = bc.getDataFile(FILE_NAME); + if ( f == null ) { + // do nothing, no file system support + return; + } + try ( final ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f)) ) { + oos.writeObject(state); + } catch ( final IOException e) { + SystemLogger.error("Unable to persist state to " + f, e); + } + } + + public Long getLastModified(final long bundleId) { + return this.bundlesLastModified.get(bundleId); + } + + public void setLastModified(final long bundleId, final long lastModified) { + this.bundlesLastModified.put(bundleId, lastModified); + } + + public void removeLastModified(final long bundleId) { + this.bundlesLastModified.remove(bundleId); + } + + public Set getKnownBundleIds() { + return this.bundlesLastModified.keySet(); + } + + public Set getEnvironments() { + return this.environments; + } + + public void changeEnvironments(final Set envs) { + this.envsChanged = this.environments.equals(envs); + this.environments.clear(); + this.environments.addAll(envs); + } + + public boolean environmentsChanged() { + return this.envsChanged; + } + + public Set getInitialHashes() { + return this.initialHashes; + } + + public void setInitialHashes(final Set value) { + this.initialHashes = value; + } + + /** + * Add all configurations for a pid + * @param pid The pid + * @param configs The list of configurations + */ + public void addAll(final String pid, final ConfigList configs) { + if ( configs != null ) { + ConfigList list = this.getConfigurations().get(pid); + if ( list == null ) { + list = new ConfigList(); + this.getConfigurations().put(pid, list); + } + + list.addAll(configs); + } + } + + /** + * Mark all configurations from that bundle as changed to reprocess them + * @param bundleId The bundle id + */ + public void checkEnvironments(final long bundleId) { + for(final String pid : this.getPids()) { + final ConfigList configList = this.getConfigurations(pid); + for(final Config cfg : configList) { + if ( cfg.getBundleId() == bundleId ) { + configList.setHasChanges(true); + break; + } + } + } + } + + @Override + public String toString() { + return "State [bundlesLastModified=" + bundlesLastModified + ", environments=" + environments + + ", initialHashes=" + initialHashes + "]"; + } +} Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/yaml/Util.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/yaml/Util.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/yaml/Util.java (added) +++ felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/yaml/Util.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,466 @@ +/* + * 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.felix.configurator.impl.yaml; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.felix.configurator.impl.logger.SystemLogger; +import org.apache.felix.configurator.impl.model.BundleState; +import org.apache.felix.configurator.impl.model.Config; +import org.apache.felix.configurator.impl.model.ConfigPolicy; +import org.apache.felix.configurator.impl.model.ConfigurationFile; +import org.apache.felix.converter.impl.ConverterService; +import org.osgi.framework.Bundle; +import org.osgi.service.converter.Converter; +import org.osgi.service.converter.TypeReference; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; + +public class Util { + + private static final String INTERNAL_PREFIX = ":configurator:"; + + private static final String PROP_VERSION = INTERNAL_PREFIX + "version"; + + private static final String PROP_ENVIRONMENTS = "environments"; + + private static final String PROP_RANKING = "ranking"; + + private static final String PROP_POLICY = "policy"; + + public static Converter getConverter() { + return new ConverterService(); // TODO use OSGi service + } + + public static BundleState readConfigurationsFromBundle(final Bundle bundle, final Set paths) { + final BundleState config = new BundleState(); + + final List allFiles = new ArrayList<>(); + for(final String path : paths) { + final List files = org.apache.felix.configurator.impl.yaml.Util.readYAML(bundle, path); + allFiles.addAll(files); + } + Collections.sort(allFiles); + + config.addFiles(allFiles); + + return config; + } + + /** + * Read all yaml files from a given path in the bundle + * @param bundle The bundle + * @param path The path + * @return A list of configuration files - sorted by url, might be empty. + */ + public static List readYAML(final Bundle bundle, final String path) { + final List result = new ArrayList<>(); + final Enumeration urls = bundle.findEntries(path, "*.yaml", false); + if ( urls != null ) { + while ( urls.hasMoreElements() ) { + final URL url = urls.nextElement(); + + final String filePath = url.getPath(); + final int pos = filePath.lastIndexOf('/'); + final String name = path + filePath.substring(pos); + + final String contents = getResource(name, url); + if ( contents != null ) { + final ConfigurationFile file = readYAML(name, url, bundle.getBundleId(), contents); + if ( file != null ) { + result.add(file); + } + } + } + Collections.sort(result); + } else { + SystemLogger.error("No configurations found at path " + path); + } + return result; + } + + /** + * Read a single YAML file + * @param name The name of the file + * @param url The url to that file or {@code null} + * @param bundleId The bundle id of the bundle containing the file + * @param contents The contents of the file + * @return The configuration file or {@code null}. + */ + public static ConfigurationFile readYAML(final String name, final URL url, final long bundleId, final String contents) { + final String identifier = (url == null ? name : url.toString()); + final Map yaml = parseYAML(name, contents); + final List configs = verifyYAML(name, yaml); + if ( configs != null ) { + final List configurations = new ArrayList<>(); + for(final Object obj : configs) { + if ( ! (obj instanceof Map) ) { + SystemLogger.error("Ignoring configuration in '" + identifier + "' (not a configuration) : " + obj); + } else { + @SuppressWarnings("unchecked") + final Map mainMap = (Map)obj; + if ( mainMap.size() != 1 ) { + SystemLogger.error("Ignoring configuration in '" + identifier + "' (more than one PID) : " + obj); + } else { + final String pid = mainMap.keySet().iterator().next(); + final Object configObj = mainMap.values().iterator().next(); + if ( ! (configObj instanceof Map) ) { + SystemLogger.error("Ignoring configuration in '" + identifier + "' (no configuration map) : " + obj); + } else { + Set environments = null; + int ranking = 0; + ConfigPolicy policy = ConfigPolicy.DEFAULT; + + @SuppressWarnings("unchecked") + final Map config = (Map)configObj; + final Dictionary properties = new Hashtable<>(); + boolean valid = true; + for(final Map.Entry entry : config.entrySet()) { + String key = entry.getKey(); + final boolean internalKey = key.startsWith(INTERNAL_PREFIX); + if ( internalKey ) { + key = key.substring(INTERNAL_PREFIX.length()); + } + final int pos = key.indexOf(':'); + String typeInfo = null; + if ( pos != -1 ) { + typeInfo = key.substring(pos + 1); + key = key.substring(0, pos); + } + + final Object value = entry.getValue(); + + if ( internalKey ) { + // no need to do type conversion based on typeInfo for internal props, type conversion is done directly below + if ( key.equals(PROP_ENVIRONMENTS) ) { + environments = Util.getConverter().convert(value).defaultValue(null).to(new TypeReference>() {}); + if ( environments == null ) { + SystemLogger.warning("Invalid environments for configuration in '" + identifier + "' : " + pid + " - " + value); + } + } else if ( key.equals(PROP_RANKING) ) { + final Integer intObj = Util.getConverter().convert(value).defaultValue(null).to(Integer.class); + if ( intObj == null ) { + SystemLogger.warning("Invalid ranking for configuration in '" + identifier + "' : " + pid + " - " + value); + } else { + ranking = intObj.intValue(); + } + } else if ( key.equals(PROP_POLICY) ) { + final String stringVal = Util.getConverter().convert(value).defaultValue(null).to(String.class); + if ( stringVal == null ) { + SystemLogger.error("Invalid policy for configuration in '" + identifier + "' : " + pid + " - " + value); + } else { + if ( value.equals("default") || value.equals("force") ) { + policy = ConfigPolicy.valueOf(stringVal.toUpperCase()); + } else { + SystemLogger.error("Invalid policy for configuration in '" + identifier + "' : " + pid + " - " + value); + } + } + } + } else { + try { + properties.put(key, convert(value, typeInfo)); + } catch ( final IOException io ) { + SystemLogger.error("Invalid type for configuration in '" + identifier + "' : " + pid + " - " + entry.getKey()); + valid = false; + break; + } + } + } + + if ( valid ) { + final Config c = new Config(pid, environments, properties, bundleId, ranking, policy); + configurations.add(c); + } + } + } + } + } + final ConfigurationFile file = new ConfigurationFile(url, configurations); + + return file; + } + return null; + } + + public static Object convert(final Object value, final String typeInfo) throws IOException { + // TODO - binary + final String yaml = createYAML(value); + if ( typeInfo == null ) { + if ( value instanceof String || value instanceof Boolean ) { + return value; + } else if ( value instanceof Long || value instanceof Double ) { + return value; + } else if ( value instanceof Integer ) { + return ((Integer)value).longValue(); + } else if ( value instanceof Float ) { + return ((Float)value).doubleValue(); + } + if ( value instanceof List ) { + @SuppressWarnings("unchecked") + final List list = (List)value; + if ( list.isEmpty() ) { + return new String[0]; + } + final Object firstObject = list.get(0); + if ( firstObject instanceof String ) { + return getConverter().convert(list).defaultValue(yaml).to(String[].class); + } else if ( firstObject instanceof Boolean ) { + return getConverter().convert(list).defaultValue(yaml).to(Boolean[].class); + } else if ( firstObject instanceof Long || firstObject instanceof Integer ) { + return getConverter().convert(list).defaultValue(yaml).to(Long[].class); + } else if ( firstObject instanceof Double || firstObject instanceof Float ) { + return getConverter().convert(list).defaultValue(yaml).to(Double[].class); + } + } + return yaml; + } + + // scalar types and primitive types + if ( "String".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(String.class); + + } else if ( "Integer".equals(typeInfo) || "int".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Integer.class); + + } else if ( "Long".equals(typeInfo) || "long".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Long.class); + + } else if ( "Float".equals(typeInfo) || "float".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Float.class); + + } else if ( "Double".equals(typeInfo) || "double".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Double.class); + + } else if ( "Byte".equals(typeInfo) || "byte".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Byte.class); + + } else if ( "Short".equals(typeInfo) || "short".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Short.class); + + } else if ( "Character".equals(typeInfo) || "char".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Character.class); + + } else if ( "Boolean".equals(typeInfo) || "boolean".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Boolean.class); + + } + + // array of scalar types and primitive types + if ( "String[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(String[].class); + + } else if ( "Integer[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Integer[].class); + + } else if ( "int[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(int[].class); + + } else if ( "Long[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Long[].class); + + } else if ( "long[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(long[].class); + + } else if ( "Float[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Float[].class); + + } else if ( "float[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(float[].class); + + } else if ( "Double[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Double[].class); + + } else if ( "double[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(double[].class); + + } else if ( "Byte[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Byte[].class); + + } else if ( "byte[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(byte[].class); + + } else if ( "Short[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Short[].class); + + } else if ( "short[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(short[].class); + + } else if ( "Character[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Character[].class); + + } else if ( "char[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(char[].class); + + } else if ( "Boolean[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(Boolean[].class); + + } else if ( "boolean[]".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(boolean[].class); + } + + // Collections of scalar types + if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + + } else if ( "Collection".equals(typeInfo) ) { + return getConverter().convert(value).defaultValue(yaml).to(new TypeReference>() {}); + } + + // unknown type - ignore configuration + throw new IOException("Invalid type information: " + typeInfo); + } + + /** + * Parse a YAML content + * @param name The name of the file + * @param contents The contents + * @return The parsed YAML map or {@code null} on failure, + */ + @SuppressWarnings("unchecked") + public static Map parseYAML(final String name, final String contents) { + try { + final Yaml yaml = new Yaml(); + try (final StringReader reader = new StringReader(contents) ) { + final Object obj = yaml.load(reader); + if ( obj instanceof Map ) { + return (Map)obj; + } + SystemLogger.error("Invalid YAML from " + name); + } + } catch ( final YAMLException ignore ) { + SystemLogger.error("Unable to read YAML from " + name, ignore); + } + return null; + } + + private static String createYAML(final Object data) throws IOException { + final DumperOptions options = new DumperOptions(); + options.setIndent(2); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + try { + final Yaml yaml = new Yaml(options); + return yaml.dump(data); + } catch ( final YAMLException ye) { + throw new IOException(ye); + } + } + + /** + * Verify the YAML according to the rules + * @param name The YAML name + * @param root The YAML root object. + * @return List of objects if valid + */ + @SuppressWarnings("unchecked") + public static List verifyYAML(final String name, final Map root) { + if ( root == null ) { + return null; + } + final Object version = root.get(PROP_VERSION); + if ( version != null ) { + + final int v = getConverter().convert(version).defaultValue(-1).to(Integer.class); + if ( v == -1 ) { + SystemLogger.error("Invalid version information in " + name + " : " + version); + return null; + } + // we only support version 1 + if ( v != 1 ) { + SystemLogger.error("Invalid version number in " + name + " : " + version); + return null; + } + } + final Object configs = root.get("configurations"); + if ( configs == null ) { + // short cut, we just return false as we don't have to process this file + return null; + } + if ( !(configs instanceof List) ) { + SystemLogger.error("Configurations must be a list of configurations in " + name); + return null; + } + return (List) configs; + } + + /** + * Read the contents of a resource, encoded as UTF-8 + * @param name The resource name + * @param url The resource URL + * @return The contents or {@code null} + */ + public static String getResource(final String name, final URL url) { + URLConnection connection = null; + try { + connection = url.openConnection(); + + try(final BufferedReader in = new BufferedReader( + new InputStreamReader( + connection.getInputStream(), "UTF-8"))) { + + final StringBuilder sb = new StringBuilder(); + String line; + + while ((line = in.readLine()) != null) { + sb.append(line); + sb.append('\n'); + } + + return sb.toString(); + } + } catch ( final IOException ioe ) { + SystemLogger.error("Unable to read " + name, ioe); + } + return null; + } +} Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/yaml/Util.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/main/java/org/apache/felix/configurator/impl/yaml/Util.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java (added) +++ felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,152 @@ +/* + * 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.felix.configurator.impl; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.net.URL; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Vector; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.wiring.BundleRevision; +import org.osgi.resource.Requirement; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; + +public class ConfiguratorTest { + + private Configurator configurator; + + private BundleContext bundleContext; + + private ConfigurationAdmin configurationAdmin; + + @Before public void setup() { + bundleContext = mock(BundleContext.class); + when(bundleContext.getBundles()).thenReturn(new Bundle[0]); + configurationAdmin = mock(ConfigurationAdmin.class); + + configurator = new Configurator(bundleContext, configurationAdmin); + } + + private Bundle setupBundle(final long id) { + final Bundle b = mock(Bundle.class); + when(b.getBundleId()).thenReturn(id); + when(b.getLastModified()).thenReturn(5L); + final BundleRevision rev = mock(BundleRevision.class); + when(b.adapt(BundleRevision.class)).thenReturn(rev); + final Requirement req = mock(Requirement.class); + when(rev.getRequirements(Util.NS_OSGI_IMPL)).thenReturn(Collections.singletonList(req)); + final Vector urls = new Vector<>(); + urls.add(this.getClass().getResource("/bundles/" + id + ".yaml")); + when(b.findEntries("OSGI-INF/configurator", "*.yaml", false)).thenReturn(urls.elements()); + + return b; + } + + @Test public void testSimpleAddRemove() throws Exception { + final Bundle b = setupBundle(1); + + Configuration c1 = mock(Configuration.class); + Configuration c2 = mock(Configuration.class); + when(configurationAdmin.getConfiguration("a", "?")).thenReturn(c1); + when(configurationAdmin.getConfiguration("b", "?")).thenReturn(c2); + + when(c1.getChangeCount()).thenReturn(1L); + when(c2.getChangeCount()).thenReturn(1L); + configurator.processAddBundle(b); + + configurator.process(); + + when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=a)")).thenReturn(new Configuration[] {c1}); + when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=b)")).thenReturn(new Configuration[] {c2}); + + final Dictionary props1 = new Hashtable<>(); + props1.put("foo", "bar"); + verify(c1).update(props1); + final Dictionary props2 = new Hashtable<>(); + props2.put("x", "y"); + verify(c2).update(props2); + + configurator.processRemoveBundle(1); + configurator.process(); + + verify(c1).delete(); + verify(c2).delete(); + } + + @Test public void testSimpleRankingRemove() throws Exception { + final Bundle b1 = setupBundle(1); + final Bundle b2 = setupBundle(2); + + Configuration c1 = mock(Configuration.class); + Configuration c2 = mock(Configuration.class); + when(configurationAdmin.getConfiguration("a", "?")).thenReturn(c1); + when(configurationAdmin.getConfiguration("b", "?")).thenReturn(c2); + + when(c1.getChangeCount()).thenReturn(1L); + when(c2.getChangeCount()).thenReturn(1L); + configurator.processAddBundle(b2); + configurator.process(); + + when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=a)")).thenReturn(new Configuration[] {c1}); + when(configurationAdmin.listConfigurations("(" + Constants.SERVICE_PID + "=b)")).thenReturn(new Configuration[] {c2}); + + final Dictionary props1 = new Hashtable<>(); + props1.put("foo", "bar2"); + final Dictionary props2 = new Hashtable<>(); + props2.put("x", "y2"); + + configurator.processAddBundle(b1); + configurator.process(); + + final Dictionary props3 = new Hashtable<>(); + props3.put("foo", "bar"); + final Dictionary props4 = new Hashtable<>(); + props4.put("x", "y"); + + configurator.processRemoveBundle(1); + configurator.process(); + + configurator.processRemoveBundle(2); + configurator.process(); + + InOrder inorder = inOrder(c1, c2); + inorder.verify(c1).update(props1); + inorder.verify(c2).update(props2); + inorder.verify(c1).update(props3); + inorder.verify(c2).update(props4); + inorder.verify(c1).update(props1); + inorder.verify(c2).update(props2); + inorder.verify(c1).delete(); + inorder.verify(c2).delete(); + inorder.verifyNoMoreInteractions(); + } +} Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/BundleStateTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/BundleStateTest.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/BundleStateTest.java (added) +++ felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/BundleStateTest.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,66 @@ +/* + * 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.felix.configurator.impl.model; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collections; + +import org.junit.Test; + +public class BundleStateTest { + + @Test public void testReadWrite() throws Exception { + final BundleState state = new BundleState(); + + final Config c1 = new Config("a", Collections.emptySet(), null, 1, 0, ConfigPolicy.DEFAULT); + final Config c2 = new Config("b", Collections.emptySet(), null, 1, 10, ConfigPolicy.DEFAULT); + + state.add(c1); + state.add(c2); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try ( final ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(state); + } + + try ( final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + final BundleState s = (BundleState) ois.readObject(); + + assertEquals(1, s.getConfigurations("a").size()); + assertEquals(1, s.getConfigurations("b").size()); + } + } + + @Test public void testDifferentPids() { + final BundleState state = new BundleState(); + final Config c1 = new Config("a", Collections.emptySet(), null, 1, 0, ConfigPolicy.DEFAULT); + final Config c2 = new Config("b", Collections.emptySet(), null, 1, 10, ConfigPolicy.DEFAULT); + + state.add(c1); + state.add(c2); + + assertEquals(1, state.getConfigurations("a").size()); + assertEquals(1, state.getConfigurations("b").size()); + } +} Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/BundleStateTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/BundleStateTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigListTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigListTest.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigListTest.java (added) +++ felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigListTest.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,95 @@ +/* + * 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.felix.configurator.impl.model; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collections; +import java.util.Iterator; + +import org.junit.Test; + +public class ConfigListTest { + + @Test public void testReadWrite() throws Exception { + final ConfigList list = new ConfigList(); + + final Config c1 = new Config("a", Collections.singleton("e1"), + null, 10, 0, ConfigPolicy.DEFAULT); + final Config c2 = new Config("a", Collections.singleton("e1"), + null, 10, 50, ConfigPolicy.DEFAULT); + list.add(c1); + list.add(c2); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try ( final ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(list); + } + + try ( final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + final ConfigList l = (ConfigList) ois.readObject(); + + assertEquals(2, l.size()); + } + } + + @Test public void testRanking() { + final ConfigList list = new ConfigList(); + final Config c1 = new Config("a", Collections.emptySet(), null, 1, 0, ConfigPolicy.DEFAULT); + final Config c2 = new Config("a", Collections.emptySet(), null, 1, 10, ConfigPolicy.DEFAULT); + final Config c3 = new Config("a", Collections.emptySet(), null, 1, 0, ConfigPolicy.DEFAULT); + final Config c4 = new Config("a", Collections.emptySet(), null, 1, 50, ConfigPolicy.DEFAULT); + final Config c5 = new Config("a", Collections.emptySet(), null, 1, 20, ConfigPolicy.DEFAULT); + final Config c6 = new Config("a", Collections.emptySet(), null, 1, 10, ConfigPolicy.DEFAULT); + + list.add(c1); + list.add(c2); + list.add(c3); + list.add(c4); + list.add(c5); + list.add(c6); + + assertEquals(6, list.size()); + final Iterator iter = list.iterator(); + assertEquals(c4, iter.next()); + assertEquals(c5, iter.next()); + assertEquals(c2, iter.next()); + assertEquals(c6, iter.next()); + assertEquals(c1, iter.next()); + assertEquals(c3, iter.next()); + } + + @Test public void testDifferentBundleIds() { + final ConfigList list = new ConfigList(); + final Config c1 = new Config("a", Collections.emptySet(), null, 2, 10, ConfigPolicy.DEFAULT); + final Config c2 = new Config("a", Collections.emptySet(), null, 1, 10, ConfigPolicy.DEFAULT); + + list.add(c1); + list.add(c2); + + assertEquals(2, list.size()); + final Iterator iter = list.iterator(); + assertEquals(c2, iter.next()); + assertEquals(c1, iter.next()); + } +} Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigListTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigListTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigTest.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigTest.java (added) +++ felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigTest.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,101 @@ +/* + * 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.felix.configurator.impl.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.Dictionary; +import java.util.HashSet; +import java.util.Hashtable; + +import org.junit.Test; + +public class ConfigTest { + + @Test public void testActiveNoEnv() { + final Config c1 = new Config("a", null, null, 1, 0, ConfigPolicy.DEFAULT); + + assertTrue(c1.isActive(Collections.emptySet())); + assertTrue(c1.isActive(Collections.singleton("foo"))); + assertTrue(c1.isActive(new HashSet<>(Arrays.asList("foo", "bar")))); + } + + @Test public void testActiveSingleEnv() { + final Config c1 = new Config("a", Collections.singleton("foo"), null, 1, 0, ConfigPolicy.DEFAULT); + + assertFalse(c1.isActive(Collections.emptySet())); + assertTrue(c1.isActive(Collections.singleton("foo"))); + assertTrue(c1.isActive(new HashSet<>(Arrays.asList("foo", "bar")))); + assertFalse(c1.isActive(Collections.singleton("bar"))); + assertFalse(c1.isActive(new HashSet<>(Arrays.asList("you", "bar")))); + } + + @Test public void testActiveTwoEnv() { + final Config c1 = new Config("a", new HashSet<>(Arrays.asList("x", "y")), null, 1, 0, ConfigPolicy.DEFAULT); + + assertFalse(c1.isActive(Collections.emptySet())); + assertTrue(c1.isActive(Collections.singleton("x"))); + assertTrue(c1.isActive(Collections.singleton("y"))); + assertTrue(c1.isActive(new HashSet<>(Arrays.asList("x", "y")))); + assertFalse(c1.isActive(new HashSet<>(Arrays.asList("a", "b")))); + } + + @Test public void testReadWrite() throws Exception { + final Dictionary props = new Hashtable<>(); + props.put("x", "1"); + props.put("y", 1L); + + final Config cfg = new Config("a", Collections.singleton("e1"), + props, 10, 50, ConfigPolicy.DEFAULT); + cfg.setIndex(70); + cfg.setState(ConfigState.UNINSTALL); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try ( final ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(cfg); + } + + try ( final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + final Config c = (Config) ois.readObject(); + + assertEquals("a", c.getPid()); + + assertEquals(10, c.getBundleId()); + assertEquals(50, c.getRanking()); + assertEquals(70, c.getIndex()); + assertEquals(ConfigState.UNINSTALL, c.getState()); + assertEquals(ConfigPolicy.DEFAULT, c.getPolicy()); + + assertEquals(2, c.getProperties().size()); + assertEquals("1", c.getProperties().get("x")); + assertEquals(1L, c.getProperties().get("y")); + + assertEquals(1, c.getEnvironments().size()); + assertEquals("e1", c.getEnvironments().iterator().next()); + } + } +} Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/ConfigTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/StateTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/StateTest.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/StateTest.java (added) +++ felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/StateTest.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,72 @@ +/* + * 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.felix.configurator.impl.model; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collections; + +import org.junit.Test; + +public class StateTest { + + @Test public void testReadWrite() throws Exception { + final State state = new State(); + + final Config c1 = new Config("a", Collections.emptySet(), null, 1, 0, ConfigPolicy.DEFAULT); + final Config c2 = new Config("b", Collections.emptySet(), null, 1, 10, ConfigPolicy.DEFAULT); + + state.add(c1); + state.add(c2); + + state.setLastModified(1, 5); + state.setLastModified(2, 15); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try ( final ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(state); + } + + try ( final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + final State s = (State) ois.readObject(); + + assertEquals(1, s.getConfigurations("a").size()); + assertEquals(1, s.getConfigurations("b").size()); + + assertEquals(5L, (Object)s.getLastModified(1)); + assertEquals(15L, (Object)s.getLastModified(2)); + } + } + + @Test public void testDifferentPids() { + final State state = new State(); + final Config c1 = new Config("a", Collections.emptySet(), null, 1, 0, ConfigPolicy.DEFAULT); + final Config c2 = new Config("b", Collections.emptySet(), null, 1, 10, ConfigPolicy.DEFAULT); + + state.add(c1); + state.add(c2); + + assertEquals(1, state.getConfigurations("a").size()); + assertEquals(1, state.getConfigurations("b").size()); + } +} Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/StateTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/model/StateTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/yaml/UtilTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/yaml/UtilTest.java?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/yaml/UtilTest.java (added) +++ felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/yaml/UtilTest.java Tue Jun 28 13:14:20 2016 @@ -0,0 +1,205 @@ +/* + * 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.felix.configurator.impl.yaml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Array; +import java.net.URL; +import java.util.Collection; +import java.util.Map; + +import org.apache.felix.configurator.impl.model.ConfigurationFile; +import org.junit.Test; + +public class UtilTest { + + /** Read the model from that name */ + public static String readYAML(final String name) throws Exception { + + try ( final Reader reader = new InputStreamReader(UtilTest.class.getResourceAsStream("/" + name), "UTF-8"); + final Writer writer = new StringWriter()) { + + final char[] buf = new char[2048]; + int len = 0; + while ((len = reader.read(buf)) > 0) { + writer.write(buf, 0, len); + } + + return writer.toString(); + } + } + + @Test public void testReadYAML() throws Exception { + final ConfigurationFile cg = Util.readYAML("a", new URL("http://a"), 1, readYAML("yaml/valid.yaml")); + assertNotNull(cg); + assertEquals(2, cg.getConfigurations().size()); + } + + @Test public void testSimpleTypeConversions() throws Exception { + final Object obj = Util.parseYAML("a", readYAML("yaml/simple-types.yaml")); + @SuppressWarnings("unchecked") + final Map config = (Map)obj; + @SuppressWarnings("unchecked") + final Map properties = (Map)config.get("config"); + + assertTrue(Util.convert(properties.get("string"), null) instanceof String); + assertTrue(Util.convert(properties.get("boolean"), null) instanceof Boolean); + assertTrue(Util.convert(properties.get("number"), null) instanceof Long); + assertTrue(Util.convert(properties.get("float"), null) instanceof Double); + + // arrays + assertTrue(Util.convert(properties.get("string.array"), null).getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("string.array"), null), 0) instanceof String); + assertTrue(Array.get(Util.convert(properties.get("string.array"), null), 1) instanceof String); + + assertTrue(Util.convert(properties.get("boolean.array"), null).getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("boolean.array"), null), 0) instanceof Boolean); + assertTrue(Array.get(Util.convert(properties.get("boolean.array"), null), 1) instanceof Boolean); + + assertTrue(Util.convert(properties.get("number.array"), null).getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("number.array"), null), 0) instanceof Long); + assertTrue(Array.get(Util.convert(properties.get("number.array"), null), 1) instanceof Long); + + assertTrue(Util.convert(properties.get("float.array"), null).getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("float.array"), null), 0) instanceof Double); + assertTrue(Array.get(Util.convert(properties.get("float.array"), null), 1) instanceof Double); + } + + @Test public void testSimpleTypeConversionsWithTypeHint() throws Exception { + final Object obj = Util.parseYAML("a", readYAML("yaml/simple-types.yaml")); + @SuppressWarnings("unchecked") + final Map config = (Map)obj; + @SuppressWarnings("unchecked") + final Map properties = (Map)config.get("config"); + + assertTrue(Util.convert(properties.get("string"), "String") instanceof String); + assertTrue(Util.convert(properties.get("boolean"), "Boolean") instanceof Boolean); + assertTrue(Util.convert(properties.get("boolean"), "boolean") instanceof Boolean); + assertTrue(Util.convert(properties.get("number"), "Integer") instanceof Integer); + assertTrue(Util.convert(properties.get("number"), "int") instanceof Integer); + assertTrue(Util.convert(properties.get("number"), "Long") instanceof Long); + assertTrue(Util.convert(properties.get("number"), "long") instanceof Long); + assertTrue(Util.convert(properties.get("float"), "Double") instanceof Double); + assertTrue(Util.convert(properties.get("float"), "double") instanceof Double); + assertTrue(Util.convert(properties.get("float"), "Float") instanceof Float); + assertTrue(Util.convert(properties.get("float"), "float") instanceof Float); + assertTrue(Util.convert(properties.get("number"), "Byte") instanceof Byte); + assertTrue(Util.convert(properties.get("number"), "byte") instanceof Byte); + assertTrue(Util.convert(properties.get("number"), "Short") instanceof Short); + assertTrue(Util.convert(properties.get("number"), "short") instanceof Short); + assertTrue(Util.convert(properties.get("string"), "Character") instanceof Character); + assertTrue(Util.convert(properties.get("string"), "char") instanceof Character); + + // arrays + assertTrue(Util.convert(properties.get("string.array"), "String[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("string.array"), "String[]"), 0) instanceof String); + assertTrue(Array.get(Util.convert(properties.get("string.array"), "String[]"), 1) instanceof String); + + assertTrue(Util.convert(properties.get("boolean.array"), "Boolean[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("boolean.array"), "Boolean[]"), 0) instanceof Boolean); + assertTrue(Array.get(Util.convert(properties.get("boolean.array"), "Boolean[]"), 1) instanceof Boolean); + + // the following would throw class cast exceptions + boolean[] a0 = (boolean[])Util.convert(properties.get("boolean.array"), "boolean[]"); + assertNotNull(a0); + int[] a1 = (int[])Util.convert(properties.get("number.array"), "int[]"); + assertNotNull(a1); + long[] a2 = (long[])Util.convert(properties.get("number.array"), "long[]"); + assertNotNull(a2); + double[] a3 = (double[])Util.convert(properties.get("float.array"), "double[]"); + assertNotNull(a3); + float[] a4 = (float[])Util.convert(properties.get("float.array"), "float[]"); + assertNotNull(a4); + byte[] a5 = (byte[])Util.convert(properties.get("number.array"), "byte[]"); + assertNotNull(a5); + short[] a6 = (short[])Util.convert(properties.get("number.array"), "short[]"); + assertNotNull(a6); + char[] a7 = (char[])Util.convert(properties.get("string.array"), "char[]"); + assertNotNull(a7); + + assertTrue(Util.convert(properties.get("number.array"), "Integer[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Integer[]"), 0) instanceof Integer); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Integer[]"), 1) instanceof Integer); + + assertTrue(Util.convert(properties.get("number.array"), "Long[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Long[]"), 0) instanceof Long); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Long[]"), 1) instanceof Long); + + assertTrue(Util.convert(properties.get("number.array"), "Byte[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Byte[]"), 0) instanceof Byte); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Byte[]"), 1) instanceof Byte); + + assertTrue(Util.convert(properties.get("number.array"), "Short[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Short[]"), 0) instanceof Short); + assertTrue(Array.get(Util.convert(properties.get("number.array"), "Short[]"), 1) instanceof Short); + + assertTrue(Util.convert(properties.get("float.array"), "Float[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("float.array"), "Float[]"), 0) instanceof Float); + assertTrue(Array.get(Util.convert(properties.get("float.array"), "Float[]"), 1) instanceof Float); + + assertTrue(Util.convert(properties.get("float.array"), "Double[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("float.array"), "Double[]"), 0) instanceof Double); + assertTrue(Array.get(Util.convert(properties.get("float.array"), "Double[]"), 1) instanceof Double); + + assertTrue(Util.convert(properties.get("string.array"), "Character[]").getClass().isArray()); + assertTrue(Array.get(Util.convert(properties.get("string.array"), "Character[]"), 0) instanceof Character); + assertTrue(Array.get(Util.convert(properties.get("string.array"), "Character[]"), 1) instanceof Character); + } + + @SuppressWarnings("unchecked") + @Test public void testCollectionTypeConversion() throws Exception { + final Object obj = Util.parseYAML("a", readYAML("yaml/simple-types.yaml")); + final Map config = (Map)obj; + final Map properties = (Map)config.get("config"); + + assertTrue(Util.convert(properties.get("string.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("string.array"), "Collection")).iterator().next() instanceof String); + + assertTrue(Util.convert(properties.get("number.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("number.array"), "Collection")).iterator().next() instanceof Integer); + + assertTrue(Util.convert(properties.get("number.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("number.array"), "Collection")).iterator().next() instanceof Long); + + assertTrue(Util.convert(properties.get("float.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("float.array"), "Collection")).iterator().next() instanceof Float); + + assertTrue(Util.convert(properties.get("float.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("float.array"), "Collection")).iterator().next() instanceof Double); + + assertTrue(Util.convert(properties.get("number.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("number.array"), "Collection")).iterator().next() instanceof Short); + + assertTrue(Util.convert(properties.get("number.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("number.array"), "Collection")).iterator().next() instanceof Byte); + + assertTrue(Util.convert(properties.get("string.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("string.array"), "Collection")).iterator().next() instanceof Character); + + assertTrue(Util.convert(properties.get("boolean.array"), "Collection") instanceof Collection); + assertTrue(((Collection)Util.convert(properties.get("boolean.array"), "Collection")).iterator().next() instanceof Boolean); + } +} Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/yaml/UtilTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/sandbox/cziegeler/configurator/src/test/java/org/apache/felix/configurator/impl/yaml/UtilTest.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Added: felix/sandbox/cziegeler/configurator/src/test/resources/bundles/1.yaml URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/resources/bundles/1.yaml?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/resources/bundles/1.yaml (added) +++ felix/sandbox/cziegeler/configurator/src/test/resources/bundles/1.yaml Tue Jun 28 13:14:20 2016 @@ -0,0 +1,5 @@ +configurations: + - a: + foo: bar + - b: + x: y Added: felix/sandbox/cziegeler/configurator/src/test/resources/bundles/2.yaml URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/resources/bundles/2.yaml?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/resources/bundles/2.yaml (added) +++ felix/sandbox/cziegeler/configurator/src/test/resources/bundles/2.yaml Tue Jun 28 13:14:20 2016 @@ -0,0 +1,5 @@ +configurations: + - a: + foo: bar2 + - b: + x: y2 Added: felix/sandbox/cziegeler/configurator/src/test/resources/yaml/simple-types.yaml URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/resources/yaml/simple-types.yaml?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/resources/yaml/simple-types.yaml (added) +++ felix/sandbox/cziegeler/configurator/src/test/resources/yaml/simple-types.yaml Tue Jun 28 13:14:20 2016 @@ -0,0 +1,17 @@ +config: + string: bar + boolean: true + number: 17 + float: 5.0 + string.array: + - a + - b + boolean.array: + - true + - false + number.array: + - 3 + - 4 + float.array: + - 1.0 + - 2.0 Added: felix/sandbox/cziegeler/configurator/src/test/resources/yaml/valid.yaml URL: http://svn.apache.org/viewvc/felix/sandbox/cziegeler/configurator/src/test/resources/yaml/valid.yaml?rev=1750501&view=auto ============================================================================== --- felix/sandbox/cziegeler/configurator/src/test/resources/yaml/valid.yaml (added) +++ felix/sandbox/cziegeler/configurator/src/test/resources/yaml/valid.yaml Tue Jun 28 13:14:20 2016 @@ -0,0 +1,5 @@ +configurations: + - a: + foo: bar + - b: + x: y