Java tutorial
/** * Copyright (c) 2009, 2012 AGETO Service GmbH and others. * All rights reserved. * * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Gunnar Wagenknecht - initial API and implementation */ package org.eclipse.gyrex.context.internal.preferences; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.annotation.PreDestroy; import javax.inject.Inject; import org.eclipse.gyrex.context.IRuntimeContext; import org.eclipse.gyrex.context.internal.ContextDebug; import org.eclipse.gyrex.context.internal.configuration.ContextConfiguration; import org.eclipse.gyrex.context.preferences.IRuntimeContextPreferences; import org.eclipse.gyrex.preferences.CloudScope; import org.eclipse.gyrex.preferences.internal.util.EclipsePreferencesUtil; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang.text.StrBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The {@link IRuntimeContextPreferences} implementation. */ public class GyrexContextPreferencesImpl implements IRuntimeContextPreferences { private static final Logger LOG = LoggerFactory.getLogger(GyrexContextPreferencesImpl.class); public static final String SETTINGS = ".settings"; private static final String EMPTY = ""; private static final String DEFAULT = "default"; private static final String UTF_8 = "UTF-8"; /** * Appends a node to the list of preferences if (and only if) the specified * path name maps to an existing node. * * @param result * the list of preferences * @param parent * the parent node * @param pathName * the path name */ private static void appendIfPathExists(final List<Preferences> result, final Preferences parent, final String pathName) { try { if (parent.nodeExists(pathName)) { if (ContextDebug.preferencesLookup) { LOG.debug("Adding node {}", parent.node(pathName).absolutePath()); } result.add(parent.node(pathName)); } else { if (ContextDebug.preferencesLookup) { LOG.debug("Ignoring node {} (does not exist)", parent.absolutePath() + "/" + pathName); } } } catch (final BackingStoreException e) { // node has been removed; if (ContextDebug.preferencesLookup) { LOG.debug("Ignoring node {} (has been removed {})", parent.absolutePath() + "/" + pathName, e.getMessage()); } } } /** * Returns the node that may be used directly for storing the specified * preferences key * * @param qualifier * a namespace qualifier for the preference (eg. typically the * symbolic name of the bundle defining the preference) * @param key * the name of the preference (optionally including its path) * @param context * the context * @return the preferences node for the context. */ public static Preferences getNode(final String qualifier, final String key, final IPath contextPath) { final String pathToPreferencesKey = getPathToPreferencesKey(qualifier, key); final Preferences node = ContextConfiguration.getRootNodeForContextPreferences(); if (!contextPath.isEmpty() && !contextPath.isRoot()) { return node.node(getPreferencesPathToSettings(contextPath, pathToPreferencesKey)); } // fallback to root return node.node(getPreferencesPathToSettings(Path.ROOT, pathToPreferencesKey)); } /** * Convenience method for {@link #getNode(String, String, IPath)} which * obtains the path from a specified {@link IRuntimeContext}. * * @param qualifier * a namespace qualifier for the preference (eg. typically the * symbolic name of the bundle defining the preference) * @param key * the name of the preference (optionally including its path) * @param context * the context * @return the preferences node for the context. */ public static Preferences getNode(final String qualifier, final String key, final IRuntimeContext context) { if (context == null) { throw new IllegalStateException("context had been disposed"); } final IPath contextPath = context.getContextPath(); return getNode(qualifier, key, contextPath); } /** * Calculates the nodes that should be searched for retrieving the specified * preference. * <p> * The lookup order is as follows: * </p> * <ul> * <li>PLATFORM Scope * <ul> * <li>[context path/.../...]/.settings/[key]</li> * <li>[context path/...]/.settings/[key]</li> * <li>[context path]/.settings/[key]</li> * <li>/.settings/[key]</li> * </ul> * </li> * <li>DEFAULT Scope * <ul> * <li>/[qualifier path]/[key]</li> * </ul> * </li> * </ul> * <p> * Thus, the deeper the context path, the more expensive a lookup might get. * </p> * * @param qualifier * a namespace qualifier for the preference (eg. typically the * symbolic name of the bundle defining the preference) * @param key * the name of the preference (optionally including its path) * @param context * the context * @return a list of preferences nodes (maybe <code>null</code>) */ public static Preferences[] getNodes(final String qualifier, final String key, final IRuntimeContext context) { if (context == null) { throw new IllegalStateException("context had been disposed"); } if (ContextDebug.preferencesLookup) { LOG.debug("Preferences lookup for {}/{} in context {}", new Object[] { qualifier, key, context }); } final IEclipsePreferences rootNode = EclipsePreferencesUtil.getRootNode(); final String pathToPreferencesKey = getPathToPreferencesKey(qualifier, key); final List<Preferences> result = new ArrayList<Preferences>(); // build lookup tree from CLOUD preferences final Preferences platformPrefRoot = rootNode.node(CloudScope.NAME) .node(ContextConfiguration.CONTEXT_PREF_ROOT.toString()); for (IPath contextPath = context.getContextPath(); !contextPath.isEmpty() && !contextPath.isRoot(); contextPath = contextPath.removeLastSegments(1)) { appendIfPathExists(result, platformPrefRoot, getPreferencesPathToSettings(contextPath, pathToPreferencesKey)); } // append always the root preference node appendIfPathExists(result, platformPrefRoot, getPreferencesPathToSettings(Path.ROOT, pathToPreferencesKey)); // append always the default scope appendIfPathExists(result, rootNode.node(DEFAULT), pathToPreferencesKey); // done return result.isEmpty() ? null : result.toArray(new Preferences[result.size()]); } public static String getPathToPreferencesKey(final String qualifier, final String key) { // REMINDER: all paths must be interpreted relative in order to prevent clients from looking at nodes higher up!!! // (IEclipsePreferences interprets absolute paths as relative to the ROOT) return EclipsePreferencesUtil.decodePath(qualifier)[1] + IPath.SEPARATOR + EclipsePreferencesUtil.makeRelative(EclipsePreferencesUtil.decodePath(key)[0]); } public static String getPreferencesPathToSettings(final IPath contextPath, final String pathToPreferencesKey) { if ((null != pathToPreferencesKey) && (pathToPreferencesKey.length() > 0)) { return contextPath.append(GyrexContextPreferencesImpl.SETTINGS).append(pathToPreferencesKey) .makeRelative().toString(); } else { return contextPath.append(GyrexContextPreferencesImpl.SETTINGS).makeRelative().toString(); } } private volatile IRuntimeContext context; /** * Creates a new instance. * * @param context */ @Inject public GyrexContextPreferencesImpl(final IRuntimeContext context) { this.context = context; } @PreDestroy public void dispose() { context = null; } public void dump(final int ident, final StrBuilder dump) { final String path = getPreferencesPathToSettings(context.getContextPath(), null); final IEclipsePreferences root = ContextConfiguration.getRootNodeForContextPreferences(); try { if (!root.nodeExists(path)) { dump.appendPadding(ident, ' ').appendln("(none)"); return; } dump(ident, dump, root.node(path)); } catch (final BackingStoreException e) { dump.appendPadding(ident, ' ').appendln(e.getMessage()); } } /** * @param ident * @param dump * @param node * @throws BackingStoreException */ private void dump(final int ident, final StrBuilder dump, final Preferences node) throws BackingStoreException { dump.appendPadding(ident, ' ').append(node.name()); final String[] keys = node.keys(); Arrays.sort(keys); for (final String key : keys) { dump.appendPadding(ident + 1, ' ').append(key).append('=').appendln(node.get(key, null)); } final String[] childrenNames = node.childrenNames(); Arrays.sort(childrenNames); for (final String name : childrenNames) { dump(ident + 1, dump, node.node(name)); } } @Override public void flush(final String qualifier) throws BackingStoreException, SecurityException { if (ContextDebug.preferencesModify) { LOG.debug("Flushing preferences of context \"{}\" for qualifier \"{}\"", context, qualifier); } getNode(qualifier, EMPTY, context).flush(); } private String get(final String key, final String defaultValue, final Preferences[] nodes) { return EclipsePreferencesUtil.getPreferencesService().get(EclipsePreferencesUtil.decodePath(key)[1], defaultValue, nodes); } @Override public String get(final String qualifier, final String key, final String defaultValue) throws SecurityException { return get(key, defaultValue, getNodes(qualifier, key, context)); } @Override public boolean getBoolean(final String qualifier, final String key, final boolean defaultValue) throws SecurityException { final String result = get(key, null, getNodes(qualifier, key, context)); return result == null ? defaultValue : Boolean.valueOf(result).booleanValue(); } @Override public byte[] getByteArray(final String qualifier, final String key, final byte[] defaultValue) throws SecurityException { final String result = get(key, null, getNodes(qualifier, key, context)); try { return result == null ? defaultValue : Base64.decodeBase64(result.getBytes(UTF_8)); } catch (final UnsupportedEncodingException e) { throw new IllegalStateException("Gyrex requires a platform which supports UTF-8.", e); } } @Override public double getDouble(final String qualifier, final String key, final double defaultValue) throws SecurityException { final String value = get(key, null, getNodes(qualifier, key, context)); return NumberUtils.toDouble(value, defaultValue); } @Override public float getFloat(final String qualifier, final String key, final float defaultValue) throws SecurityException { final String value = get(key, null, getNodes(qualifier, key, context)); return NumberUtils.toFloat(value, defaultValue); } @Override public int getInt(final String qualifier, final String key, final int defaultValue) throws SecurityException { final String value = get(key, null, getNodes(qualifier, key, context)); return NumberUtils.toInt(value, defaultValue); } @Override public String[] getKeys(final String qualifier) throws BackingStoreException, SecurityException { // simply lookup the node using an empty key return getNode(qualifier, StringUtils.EMPTY, context).keys(); } @Override public long getLong(final String qualifier, final String key, final long defaultValue) throws SecurityException { final String value = get(key, null, getNodes(qualifier, key, context)); return NumberUtils.toLong(value, defaultValue); } @Override public void put(final String qualifier, final String key, final String value, final boolean encrypt) throws SecurityException { if (null == value) { remove(qualifier, key); } else { put(qualifier, key, value, context, encrypt); } } private void put(final String qualifier, final String key, final String value, final IRuntimeContext context, final boolean encrypt) { if (ContextDebug.preferencesModify) { LOG.debug("Preference modification in context \"{}\" for qualifier \"{}\": {}", new Object[] { context, qualifier, key }); } getNode(qualifier, key, context).put(EclipsePreferencesUtil.decodePath(key)[1], value); } @Override public void putBoolean(final String qualifier, final String key, final boolean value, final boolean encrypt) throws SecurityException { put(qualifier, key, Boolean.toString(value), context, encrypt); } @Override public void putByteArray(final String qualifier, final String key, final byte[] value, final boolean encrypt) throws SecurityException { if (null == value) { remove(qualifier, key); } else { try { put(qualifier, key, new String(Base64.encodeBase64(value), UTF_8), context, encrypt); } catch (final UnsupportedEncodingException e) { throw new IllegalStateException("Gyrex requires a platform which supports UTF-8.", e); } } } @Override public void putDouble(final String qualifier, final String key, final double value, final boolean encrypt) throws SecurityException { put(qualifier, key, Double.toString(value), context, encrypt); } @Override public void putFloat(final String qualifier, final String key, final float value, final boolean encrypt) throws SecurityException { put(qualifier, key, Float.toString(value), context, encrypt); } @Override public void putInt(final String qualifier, final String key, final int value, final boolean encrypt) throws SecurityException { put(qualifier, key, Integer.toString(value), context, encrypt); } @Override public void putLong(final String qualifier, final String key, final long value, final boolean encrypt) throws SecurityException { put(qualifier, key, Long.toString(value), context, encrypt); } @Override public void remove(final String qualifier, final String key) throws SecurityException { getNode(qualifier, key, context).remove(EclipsePreferencesUtil.decodePath(key)[1]); } @Override public void sync(final String qualifier) throws BackingStoreException, SecurityException { if (ContextDebug.preferencesModify) { LOG.debug("Synchronizing preferences of context \"{}\" for qualifier \"{}\"", context, qualifier); } getNode(qualifier, EMPTY, context).sync(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("GyrexContextPreferencesImpl [context=").append(context).append("]"); return builder.toString(); } }