/* * 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.karaf.config.core.impl; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import java.util.UUID; import org.apache.felix.utils.properties.Properties; import org.apache.karaf.config.core.ConfigRepository; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ConfigRepositoryImpl implements ConfigRepository { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigRepositoryImpl.class); private static final String FILEINSTALL_FILE_NAME = "felix.fileinstall.filename"; private ConfigurationAdmin configAdmin; private File storage; public ConfigRepositoryImpl(ConfigurationAdmin configAdmin) { this(configAdmin, null); } public ConfigRepositoryImpl(ConfigurationAdmin configAdmin, File storage) { this.configAdmin = configAdmin; this.storage = storage; } /* (non-Javadoc) * @see org.apache.karaf.shell.config.impl.ConfigRepository#update(java.lang.String, java.util.Dictionary, boolean) */ @Override public void update(String pid, Dictionary<String, Object> properties) throws IOException { LOGGER.trace("Update configuration {}", pid); Configuration cfg = configAdmin.getConfiguration(pid, null); if (storage != null) { // Check, whether a file location is already provided. if (properties.get(FILEINSTALL_FILE_NAME) == null) { String cfgFileName = pid + ".cfg"; File cfgFile = new File(storage, cfgFileName); properties.put(FILEINSTALL_FILE_NAME, cfgFile.getCanonicalFile().toURI().toString()); } } cfg.update(properties); } /* (non-Javadoc) * @see org.apache.karaf.shell.config.impl.ConfigRepository#delete(java.lang.String) */ @Override public void delete(String pid) throws Exception { LOGGER.trace("Delete configuration {}", pid); Configuration configuration = configAdmin.getConfiguration(pid, null); configuration.delete(); try { deleteStorage(pid); } catch (Exception e) { LOGGER.warn("Can't delete cfg file", e); } } protected void deleteStorage(String pid) throws Exception { if (storage != null) { File cfgFile = new File(storage, pid + ".cfg"); LOGGER.trace("Delete {}", cfgFile.getName()); cfgFile.delete(); } } private File getCfgFileFromProperties(Dictionary<String, Object> properties) throws URISyntaxException, MalformedURLException { File cfgFile = null; if (properties != null) { Object val = properties.get(FILEINSTALL_FILE_NAME); if (val instanceof URL) { cfgFile = new File(((URL) val).toURI()); } if (val instanceof URI) { cfgFile = new File((URI) val); } if (val instanceof String) { cfgFile = new File(new URL((String) val).toURI()); } } return cfgFile; } protected void updateStorage(String pid, Dictionary<String, Object> props) throws IOException { if (storage != null) { Configuration cfg = configAdmin.getConfiguration(pid, null); // Initialize cfgFile with default location. Value gets overwritten when the existing configuration references a correct location. File cfgFile = new File(storage, pid + ".cfg"); if (cfg != null) { Dictionary<String, Object> oldProps = cfg.getProperties(); if (oldProps != null && oldProps.get(FILEINSTALL_FILE_NAME) != null) { try { cfgFile = getCfgFileFromProperties(oldProps); if (cfgFile == null) { throw new IOException("The configuration value '" + oldProps.get(FILEINSTALL_FILE_NAME) + "' for '" + FILEINSTALL_FILE_NAME + "' does not represent a valid file location."); } } catch (URISyntaxException | MalformedURLException e) { throw new IOException(e); } } } LOGGER.trace("Update {}", cfgFile.getName()); // update the cfg file Properties properties = new Properties(cfgFile); for (Enumeration<String> keys = props.keys(); keys.hasMoreElements(); ) { String key = keys.nextElement(); if (!Constants.SERVICE_PID.equals(key) && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) && !FILEINSTALL_FILE_NAME.equals(key)) { if (props.get(key) != null) { properties.put(key, props.get(key).toString()); } } } // remove "removed" properties from the cfg file ArrayList<String> propertiesToRemove = new ArrayList<>(); for (String key : properties.keySet()) { if (props.get(key) == null && !Constants.SERVICE_PID.equals(key) && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) && !FILEINSTALL_FILE_NAME.equals(key)) { propertiesToRemove.add(key); } } for (String key : propertiesToRemove) { properties.remove(key); } // save the cfg file storage.mkdirs(); properties.save(); } } /* (non-Javadoc) * @see org.apache.karaf.shell.config.impl.ConfigRepository#getConfigProperties(java.lang.String) */ @Override public Dictionary<String, Object> getConfigProperties(String pid) throws IOException, InvalidSyntaxException { if (pid != null && configAdmin != null) { Configuration configuration = configAdmin.getConfiguration(pid, null); if (configuration != null) { Dictionary<String, Object> props = configuration.getProperties(); return (props != null) ? props : new Hashtable<>(); } } return null; } @Override public ConfigurationAdmin getConfigAdmin() { return configAdmin; } @Override public String createFactoryConfiguration(String factoryPid, Dictionary<String, Object> properties) { return createFactoryConfiguration(factoryPid, null, properties); } @Override public String createFactoryConfiguration(String factoryPid, String alias, Dictionary<String, Object> properties) { try { Configuration config = configAdmin.createFactoryConfiguration(factoryPid, null); if (storage != null) { // Check, whether a file location is already provided. if (properties.get(FILEINSTALL_FILE_NAME) == null) { // Create a synthetic unique alias for the factory // configuration when it is unspecified. if (alias == null) { // Felix Fileinstall uses the hyphen as separator // between factoryPid and alias. For safety reasons, all // hyphens are removed from the generated UUID. alias = UUID.randomUUID().toString().replaceAll("-", ""); } String cfgFileName = factoryPid + "-" + alias + ".cfg"; File cfgFile = new File(storage, cfgFileName); properties.put(FILEINSTALL_FILE_NAME, cfgFile.getCanonicalFile().toURI().toString()); } } config.update(properties); String pid = config.getPid(); // updateStorage(pid, properties); return pid; } catch (IOException e) { throw new UncheckedIOException(e); } } }