/* * 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.settings.impl; 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.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.sling.settings.SlingSettingsService; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the basic implementation of the sling settings service. */ public class SlingSettingsServiceImpl implements SlingSettingsService { /** Property containing the sling name. */ private static final String SLING_NAME = "sling.name"; /** Property containing the sling description. */ private static final String SLING_DESCRIPTION = "sling.description"; /** The logger */ private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** The sling instance id. */ private String slingId; /** The sling home */ private String slingHome; /** The sling home url */ private URL slingHomeUrl; /** The set of run modes .*/ private Set<String> runModes; /** The name of the data file holding the sling id. */ private static final String ID_FILE = "sling.id.file"; /** The name of the data file holding install run mode options */ private static final String OPTIONS_FILE = "sling.options.file"; /** The properties for name, description. */ private final Map<String, String> slingProps = new HashMap<String, String>(); /** * Create the service and search the Sling home urls and * get/create a sling id. * Setup run modes * @param context The bundle context */ public SlingSettingsServiceImpl(final BundleContext context) { this.setupSlingProps(context); this.setupSlingHome(context); this.setupSlingId(context); this.setupRunModes(context); } /** * Get sling home and sling home URL */ private void setupSlingHome(final BundleContext context) { this.slingHome = context.getProperty(SLING_HOME); final String url = context.getProperty(SLING_HOME_URL); if ( url != null ) { try { this.slingHomeUrl = new URL(url); } catch (MalformedURLException e) { logger.error("Sling home url is not a url: {}", url); } } } /** * Get / create sling id */ private void setupSlingId(final BundleContext context) { // try to read the id from the id file first final File idFile = context.getDataFile(ID_FILE); if ( idFile == null ) { // the osgi framework does not support storing something in the file system throw new RuntimeException("Unable to read from bundle data file."); } try { slingId = SlingIdUtil.readSlingId(idFile); logger.info("Read Sling ID {} from file {}", slingId, idFile); } catch (final Throwable t) { logger.error("Failed reading Sling ID from file " + idFile, t); } // no sling id yet or failure to read file: create an id and store if (slingId == null) { slingId = SlingIdUtil.createSlingId(); logger.info("Created new Sling ID {}", slingId); try { SlingIdUtil.writeSlingId(idFile, slingId); } catch (final Throwable t) { logger.error("Failed writing Sling ID to file " + idFile, t); } } } /** * Get / create sling id */ private void setupSlingProps(final BundleContext context) { synchronized ( this.slingProps ) { if ( this.slingProps.get(SLING_NAME) == null && context.getProperty(SLING_NAME) != null ) { this.slingProps.put(SLING_NAME, context.getProperty(SLING_NAME)); } if ( this.slingProps.get(SLING_DESCRIPTION) == null && context.getProperty(SLING_DESCRIPTION) != null ) { this.slingProps.put(SLING_DESCRIPTION, context.getProperty(SLING_DESCRIPTION)); } } } static final class Options implements Serializable { private static final long serialVersionUID = 1L; String[] modes; String selected; } private List<Options> handleOptions(final Set<String> modesSet, final String propOptions) { final List<Options> optionsList = new ArrayList<Options>(); if ( propOptions != null && propOptions.trim().length() > 0 ) { final String[] options = propOptions.trim().split("\\|"); for(final String opt : options) { String selected = null; final String[] modes = opt.trim().split(","); for(int i=0; i<modes.length; i++) { modes[i] = modes[i].trim(); if ( selected != null ) { modesSet.remove(modes[i]); } else { if ( modesSet.contains(modes[i]) ) { selected = modes[i]; } } } if ( selected == null ) { selected = modes[0]; modesSet.add(modes[0]); } final Options o = new Options(); o.selected = selected; o.modes = modes; optionsList.add(o); } } return optionsList; } /** * Set up run modes. */ private void setupRunModes(final BundleContext context) { final Set<String> modesSet = new HashSet<String>(); // check configuration property first final String prop = context.getProperty(RUN_MODES_PROPERTY); if (prop != null && prop.trim().length() > 0) { final String[] modes = prop.split(","); for(int i=0; i < modes.length; i++) { modesSet.add(modes[i].trim()); } } // handle configured options this.handleOptions(modesSet, context.getProperty(RUN_MODE_OPTIONS)); // handle configured install options // read persisted options if restart or update final List<Options> storedOptions = readOptions(context); if ( storedOptions != null ) { for(final Options o : storedOptions) { for(final String m : o.modes) { modesSet.remove(m); } modesSet.add(o.selected); } } // now install options final List<Options> optionsList = this.handleOptions(modesSet, context.getProperty(RUN_MODE_INSTALL_OPTIONS)); // and always save new install options writeOptions(context, optionsList); // make the set unmodifiable and synced // we probably don't need a synced set as it is read only this.runModes = Collections.synchronizedSet(Collections.unmodifiableSet(modesSet)); if ( this.runModes.size() > 0 ) { logger.info("Active run modes: {}", this.runModes); } else { logger.info("No run modes active"); } } @SuppressWarnings("unchecked") private List<Options> readOptions(final BundleContext context) { List<Options> optionsList = null; final File file = context.getDataFile(OPTIONS_FILE); if ( file.exists() ) { FileInputStream fis = null; ObjectInputStream ois = null; try { fis = new FileInputStream(file); ois = new ObjectInputStream(fis); optionsList = (List<Options>) ois.readObject(); } catch ( final IOException ioe ) { throw new RuntimeException("Unable to read from options data file.", ioe); } catch (ClassNotFoundException cnfe) { throw new RuntimeException("Unable to read from options data file.", cnfe); } finally { if ( ois != null ) { try { ois.close(); } catch ( final IOException ignore) {} } if ( fis != null ) { try { fis.close(); } catch ( final IOException ignore) {} } } } return optionsList; } void writeOptions(final BundleContext context, final List<Options> optionsList) { final File file = context.getDataFile(OPTIONS_FILE); FileOutputStream fos = null; ObjectOutputStream oos = null; try { fos = new FileOutputStream(file); oos = new ObjectOutputStream(fos); oos.writeObject(optionsList); } catch ( final IOException ioe ) { throw new RuntimeException("Unable to write to options data file.", ioe); } finally { if ( oos != null ) { try { oos.close(); } catch ( final IOException ignore) {} } if ( fos != null ) { try { fos.close(); } catch ( final IOException ignore) {} } } } /** * @see org.apache.sling.settings.SlingSettingsService#getAbsolutePathWithinSlingHome(String) */ public String getAbsolutePathWithinSlingHome(final String relativePath) { return new File(slingHome, relativePath).getAbsolutePath(); } /** * @see org.apache.sling.settings.SlingSettingsService#getSlingId() */ public String getSlingId() { return this.slingId; } /** * @see org.apache.sling.settings.SlingSettingsService#getSlingHome() */ public URL getSlingHome() { return this.slingHomeUrl; } /** * @see org.apache.sling.settings.SlingSettingsService#getSlingHomePath() */ public String getSlingHomePath() { return this.slingHome; } /** * @see org.apache.sling.settings.SlingSettingsService#getRunModes() */ public Set<String> getRunModes() { return this.runModes; } /** * @see org.apache.sling.settings.SlingSettingsService#getSlingName() */ public String getSlingName() { synchronized ( this.slingProps ) { String name = this.slingProps.get(SLING_NAME); if ( name == null ) { name = "Instance " + this.slingId; // default } return name; } } /** * @see org.apache.sling.settings.SlingSettingsService#getSlingDescription() */ public String getSlingDescription() { synchronized ( this.slingProps ) { String desc = this.slingProps.get(SLING_DESCRIPTION); if ( desc == null ) { desc = "Instance with id " + this.slingId + " and run modes " + this.getRunModes(); // default } return desc; } } /** * Update the configuration of this service */ public void update(final Dictionary<String, Object> properties) { if ( properties != null ) { synchronized ( this.slingProps ) { if ( properties.get(SLING_NAME) != null ) { this.slingProps.put(SLING_NAME, properties.get(SLING_NAME).toString()); } if ( properties.get(SLING_DESCRIPTION) != null ) { this.slingProps.put(SLING_DESCRIPTION, properties.get(SLING_DESCRIPTION).toString()); } } } } }