| /* |
| * 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.shell.config; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Dictionary; |
| import java.util.Enumeration; |
| import java.util.List; |
| |
| import org.apache.felix.fileinstall.ArtifactInstaller; |
| import org.apache.karaf.shell.console.OsgiCommandSupport; |
| import org.apache.karaf.util.Properties; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.cm.Configuration; |
| import org.osgi.service.cm.ConfigurationAdmin; |
| |
| /** |
| * Abstract class from which all commands related to the ConfigurationAdmin |
| * service should derive. |
| * This command retrieves a reference to the ConfigurationAdmin service before |
| * calling another method to actually process the command. |
| */ |
| public abstract class ConfigCommandSupport extends OsgiCommandSupport { |
| |
| public static final String PROPERTY_CONFIG_PID = "ConfigCommand.PID"; |
| public static final String PROPERTY_CONFIG_PROPS = "ConfigCommand.Props"; |
| private static final String PID_FILTER = "(service.pid=%s*)"; |
| private static final String FILE_PREFIX = "file:"; |
| private static final String CONFIG_SUFFIX = ".cfg"; |
| private static final String FACTORY_SEPARATOR = "-"; |
| private static final String FILEINSTALL_FILE_NAME = "felix.fileinstall.filename"; |
| |
| protected File storage; |
| private List<ArtifactInstaller> artifactInstallers; |
| |
| protected Object doExecute() throws Exception { |
| // Get config admin service. |
| ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName()); |
| if (ref == null) { |
| System.out.println("ConfigurationAdmin service is unavailable."); |
| return null; |
| } |
| ConfigurationAdmin admin = getConfigurationAdmin(); |
| if (admin == null) { |
| System.out.println("ConfigAdmin service is unavailable."); |
| return null; |
| } |
| |
| doExecute(admin); |
| return null; |
| } |
| |
| protected Dictionary getEditedProps() throws Exception { |
| return (Dictionary) this.session.get(PROPERTY_CONFIG_PROPS); |
| } |
| |
| protected ConfigurationAdmin getConfigurationAdmin() { |
| ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName()); |
| if (ref == null) { |
| return null; |
| } |
| try { |
| ConfigurationAdmin admin = (ConfigurationAdmin) getBundleContext().getService(ref); |
| if (admin == null) { |
| return null; |
| } else { |
| return admin; |
| } |
| } finally { |
| getBundleContext().ungetService(ref); |
| } |
| } |
| |
| protected abstract void doExecute(ConfigurationAdmin admin) throws Exception; |
| |
| /** |
| * <p> |
| * Returns the Configuration object of the given (felix fileinstall) file name. |
| * </p> |
| * |
| * @param fileName |
| * @return |
| */ |
| public Configuration findConfigurationByFileName(ConfigurationAdmin admin, String fileName) throws IOException, InvalidSyntaxException { |
| if (fileName != null && fileName.contains(FACTORY_SEPARATOR)) { |
| String factoryPid = fileName.substring(0, fileName.lastIndexOf(FACTORY_SEPARATOR)); |
| String absoluteFileName = FILE_PREFIX + storage.getAbsolutePath() + File.separator + fileName + CONFIG_SUFFIX; |
| Configuration[] configurations = admin.listConfigurations(String.format(PID_FILTER, factoryPid)); |
| if (configurations != null) { |
| for (Configuration configuration : configurations) { |
| Dictionary dictionary = configuration.getProperties(); |
| if (dictionary != null) { |
| String fileInstallFileName = (String) dictionary.get(FILEINSTALL_FILE_NAME); |
| if (absoluteFileName.equals(fileInstallFileName)) { |
| return configuration; |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Saves config to storage or ConfigurationAdmin. |
| * |
| * @param admin |
| * @param pid |
| * @param props |
| * @param bypassStorage |
| * @throws IOException |
| */ |
| protected void update(ConfigurationAdmin admin, String pid, Dictionary props, boolean bypassStorage) throws IOException { |
| if (!bypassStorage && storage != null) { |
| persistConfiguration(admin, pid, props); |
| } else { |
| updateConfiguration(admin, pid, props); |
| } |
| } |
| |
| /** |
| * Persists configuration to storage. |
| * |
| * @param admin |
| * @param pid |
| * @param props |
| * @throws IOException |
| */ |
| protected void persistConfiguration(ConfigurationAdmin admin, String pid, Dictionary props) throws IOException { |
| File storageFile = new File(storage, pid + ".cfg"); |
| Configuration cfg = admin.getConfiguration(pid, null); |
| if (cfg != null && cfg.getProperties() != null) { |
| Object val = cfg.getProperties().get(FILEINSTALL_FILE_NAME); |
| try { |
| if (val instanceof URL) { |
| storageFile = new File(((URL) val).toURI()); |
| } |
| if (val instanceof URI) { |
| storageFile = new File((URI) val); |
| } |
| if (val instanceof String) { |
| storageFile = new File(new URL((String) val).toURI()); |
| } |
| } catch (Exception e) { |
| throw (IOException) new IOException(e.getMessage()).initCause(e); |
| } |
| } |
| Properties p = new Properties(storageFile); |
| for (Enumeration keys = props.keys(); keys.hasMoreElements(); ) { |
| Object key = keys.nextElement(); |
| if (!Constants.SERVICE_PID.equals(key) |
| && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) |
| && !FILEINSTALL_FILE_NAME.equals(key)) { |
| p.put((String) key, (String) props.get(key)); |
| } |
| } |
| // remove "removed" properties from the file |
| ArrayList<String> propertiesToRemove = new ArrayList<String>(); |
| for (Object key : p.keySet()) { |
| if (props.get(key) == null |
| && !Constants.SERVICE_PID.equals(key) |
| && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) |
| && !FILEINSTALL_FILE_NAME.equals(key)) { |
| propertiesToRemove.add(key.toString()); |
| } |
| } |
| for (String key : propertiesToRemove) { |
| p.remove(key); |
| } |
| // save the cfg file |
| storage.mkdirs(); |
| p.save(); |
| updateFileInstall(storageFile); |
| } |
| |
| /** |
| * Trigger felix fileinstall to update the config so there is no delay till it polls the file |
| * |
| * @param storageFile |
| * @throws Exception |
| */ |
| private void updateFileInstall(File storageFile) { |
| if (artifactInstallers != null) { |
| for (ArtifactInstaller installer : artifactInstallers) { |
| if (installer.canHandle(storageFile)) { |
| try { |
| installer.update(storageFile); |
| } catch (Exception e) { |
| log.warn("Error updating config " + storageFile + " in felix fileinstall" + e.getMessage(), e); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Updates the configuration to the {@link ConfigurationAdmin} service. |
| * |
| * @param admin |
| * @param pid |
| * @param props |
| * @throws IOException |
| */ |
| public void updateConfiguration(ConfigurationAdmin admin, String pid, Dictionary props) throws IOException { |
| Configuration cfg = admin.getConfiguration(pid, null); |
| if (cfg.getProperties() == null) { |
| String[] pids = parsePid(pid); |
| if (pids[1] != null) { |
| cfg = admin.createFactoryConfiguration(pids[0], null); |
| } |
| } |
| if (cfg.getBundleLocation() != null) { |
| cfg.setBundleLocation(null); |
| } |
| cfg.update(props); |
| } |
| |
| protected String[] parsePid(String pid) { |
| int n = pid.indexOf('-'); |
| if (n > 0) { |
| String factoryPid = pid.substring(n + 1); |
| pid = pid.substring(0, n); |
| return new String[]{pid, factoryPid}; |
| } else { |
| return new String[]{pid, null}; |
| } |
| } |
| |
| protected void deleteStorage(String pid) throws Exception { |
| File cfgFile = new File(storage, pid + ".cfg"); |
| cfgFile.delete(); |
| } |
| |
| public File getStorage() { |
| return storage; |
| } |
| |
| public void setStorage(File storage) { |
| this.storage = storage; |
| } |
| |
| public void setArtifactInstallers(List<ArtifactInstaller> artifactInstallers) { |
| this.artifactInstallers = artifactInstallers; |
| } |
| } |