/****************************************************************************** | |
* Copyright (c) 2006, 2010 VMware Inc. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* and Apache License v2.0 which accompanies this distribution. | |
* The Eclipse Public License is available at | |
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0 | |
* is available at http://www.opensource.org/licenses/apache2.0.php. | |
* You may elect to redistribute this code under either of these licenses. | |
* | |
* Contributors: | |
* VMware Inc. | |
*****************************************************************************/ | |
package org.eclipse.gemini.blueprint.extender.support.internal; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.osgi.framework.Bundle; | |
import org.osgi.framework.Version; | |
import org.springframework.util.Assert; | |
import org.springframework.util.ObjectUtils; | |
import org.springframework.util.StringUtils; | |
import java.util.Dictionary; | |
import static org.eclipse.gemini.blueprint.io.OsgiBundleResource.BUNDLE_URL_PREFIX; | |
/** | |
* Utility class for dealing with the extender configuration and OSGi bundle | |
* manifest headers. | |
* | |
* Defines Spring/OSGi constants and methods for configuring Spring application | |
* context. | |
* | |
* @author Costin Leau | |
* | |
*/ | |
public abstract class ConfigUtils { | |
private static final Log log = LogFactory.getLog(ConfigUtils.class); | |
public static final String EXTENDER_VERSION = "SpringExtender-Version"; | |
private static final String LEFT_CLOSED_INTERVAL = "["; | |
private static final String LEFT_OPEN_INTERVAL = "("; | |
private static final String RIGHT_CLOSED_INTERVAL = "]"; | |
private static final String RIGHT_OPEN_INTERVAL = ")"; | |
private static final String COMMA = ","; | |
public static final String CONFIG_WILDCARD = "*"; | |
/** | |
* Manifest entry name for configuring Spring application context. | |
*/ | |
public static final String SPRING_CONTEXT_HEADER = "Spring-Context"; | |
public static final String BLUEPRINT_SPEC_CONTEXT_LOCATION = BUNDLE_URL_PREFIX + "/OSGI-INF/blueprint/*.xml"; | |
public static final String LEGACY_SPRING_DM_CONTEXT_LOCATION = BUNDLE_URL_PREFIX + "/META-INF/spring/*.xml"; | |
/** | |
* Directive for publishing Spring application context as a service. | |
*/ | |
public static final String DIRECTIVE_PUBLISH_CONTEXT = "publish-context"; | |
/** | |
* Directive for indicating wait-for time when satisfying mandatory | |
* dependencies defined in seconds | |
*/ | |
public static final String DIRECTIVE_TIMEOUT = "timeout"; | |
public static final String DIRECTIVE_TIMEOUT_VALUE_NONE = "none"; | |
/** | |
* Create asynchronously directive. | |
*/ | |
public static final String DIRECTIVE_CREATE_ASYNCHRONOUSLY = "create-asynchronously"; | |
/** | |
* Wait for dependencies or directly start the context. | |
*/ | |
public static final String DIRECTIVE_WAIT_FOR_DEPS = "wait-for-dependencies"; | |
/** | |
* {@link #DIRECTIVE_WAIT_FOR_DEPS} default. | |
*/ | |
public static final boolean DIRECTIVE_WAIT_FOR_DEPS_DEFAULT = true; | |
public static final String EQUALS = ":="; | |
/** | |
* Token used for separating directives inside a header. | |
*/ | |
public static final String DIRECTIVE_SEPARATOR = ";"; | |
public static final String CONTEXT_LOCATION_SEPARATOR = ","; | |
public static final boolean DIRECTIVE_PUBLISH_CONTEXT_DEFAULT = true; | |
public static final boolean DIRECTIVE_CREATE_ASYNCHRONOUSLY_DEFAULT = true; | |
public static final long DIRECTIVE_TIMEOUT_DEFAULT = 5 * 60; // 5 minutes | |
public static final long DIRECTIVE_NO_TIMEOUT = -2L; // Indicates wait forever | |
public static boolean matchExtenderVersionRange(Bundle bundle, String header, Version versionToMatch) { | |
Assert.notNull(bundle); | |
// get version range | |
String range = (String) bundle.getHeaders().get(header); | |
boolean trace = log.isTraceEnabled(); | |
// empty value = empty version = * | |
if (!StringUtils.hasText(range)) | |
return true; | |
if (trace) | |
log.trace("discovered " + header + " header w/ value=" + range); | |
// do we have a range or not ? | |
range = StringUtils.trimWhitespace(range); | |
// a range means one comma | |
int commaNr = StringUtils.countOccurrencesOf(range, COMMA); | |
// no comma, no intervals | |
if (commaNr == 0) { | |
Version version = Version.parseVersion(range); | |
return versionToMatch.equals(version); | |
} | |
if (commaNr == 1) { | |
// sanity check | |
if (!((range.startsWith(LEFT_CLOSED_INTERVAL) || range.startsWith(LEFT_OPEN_INTERVAL)) && (range.endsWith(RIGHT_CLOSED_INTERVAL) || range.endsWith(RIGHT_OPEN_INTERVAL)))) { | |
throw new IllegalArgumentException("range [" + range + "] is invalid"); | |
} | |
boolean equalMin = range.startsWith(LEFT_CLOSED_INTERVAL); | |
boolean equalMax = range.endsWith(RIGHT_CLOSED_INTERVAL); | |
// remove interval brackets | |
range = range.substring(1, range.length() - 1); | |
// split the remaining string in two pieces | |
String[] pieces = StringUtils.split(range, COMMA); | |
if (trace) | |
log.trace("discovered low/high versions : " + ObjectUtils.nullSafeToString(pieces)); | |
Version minVer = Version.parseVersion(pieces[0]); | |
Version maxVer = Version.parseVersion(pieces[1]); | |
if (trace) | |
log.trace("comparing version " + versionToMatch + " w/ min=" + minVer + " and max=" + maxVer); | |
boolean result = true; | |
int compareMin = versionToMatch.compareTo(minVer); | |
if (equalMin) | |
result = (result && (compareMin >= 0)); | |
else | |
result = (result && (compareMin > 0)); | |
int compareMax = versionToMatch.compareTo(maxVer); | |
if (equalMax) | |
result = (result && (compareMax <= 0)); | |
else | |
result = (result && (compareMax < 0)); | |
return result; | |
} | |
// more then one comma means incorrect range | |
throw new IllegalArgumentException("range [" + range + "] is invalid"); | |
} | |
/** | |
* Return the {@value #SPRING_CONTEXT_HEADER} if present from the given | |
* dictionary. | |
* | |
* @param headers | |
* @return | |
*/ | |
public static String getSpringContextHeader(Dictionary headers) { | |
Object header = null; | |
if (headers != null) | |
header = headers.get(SPRING_CONTEXT_HEADER); | |
return (header != null ? header.toString().trim() : null); | |
} | |
/** | |
* Return the directive value as a String. If the directive does not exist | |
* or is invalid (wrong format) a null string will be returned. | |
* | |
* @param header | |
* @param directive | |
* @return | |
*/ | |
public static String getDirectiveValue(String header, String directive) { | |
Assert.notNull(header, "not-null header required"); | |
Assert.notNull(directive, "not-null directive required"); | |
String[] directives = StringUtils.tokenizeToStringArray(header, DIRECTIVE_SEPARATOR); | |
for (int i = 0; i < directives.length; i++) { | |
String[] splittedDirective = StringUtils.delimitedListToStringArray(directives[i].trim(), EQUALS); | |
if (splittedDirective.length == 2 && splittedDirective[0].equals(directive)) | |
return splittedDirective[1]; | |
} | |
return null; | |
} | |
/** | |
* Shortcut method to retrieve directive values. Used internally by the | |
* dedicated getXXX. | |
* | |
* @param directiveName | |
* @return | |
*/ | |
private static String getDirectiveValue(Dictionary headers, String directiveName) { | |
String header = getSpringContextHeader(headers); | |
if (header != null) { | |
String directive = getDirectiveValue(header, directiveName); | |
if (directive != null) | |
return directive; | |
} | |
return null; | |
} | |
/** | |
* Returns true if the given directive is present or false otherwise. | |
* | |
* @param headers | |
* @param directiveName | |
* @return | |
*/ | |
public static boolean isDirectiveDefined(Dictionary headers, String directiveName) { | |
String header = getSpringContextHeader(headers); | |
if (header != null) { | |
String directive = getDirectiveValue(header, directiveName); | |
return (directive != null); | |
} | |
return false; | |
} | |
/** | |
* Shortcut for finding the boolean value for | |
* {@link #DIRECTIVE_PUBLISH_CONTEXT} directive using the given headers. | |
* Assumes the headers belong to a Spring powered bundle. | |
* | |
* @param headers | |
* @return | |
*/ | |
public static boolean getPublishContext(Dictionary headers) { | |
String value = getDirectiveValue(headers, DIRECTIVE_PUBLISH_CONTEXT); | |
return (value != null ? Boolean.valueOf(value).booleanValue() : DIRECTIVE_PUBLISH_CONTEXT_DEFAULT); | |
} | |
/** | |
* Shortcut for finding the boolean value for | |
* {@link #DIRECTIVE_CREATE_ASYNCHRONOUSLY} directive using the given | |
* headers. | |
* | |
* Assumes the headers belong to a Spring powered bundle. | |
* | |
* @param headers | |
* @return | |
*/ | |
public static boolean getCreateAsync(Dictionary headers) { | |
String value = getDirectiveValue(headers, DIRECTIVE_CREATE_ASYNCHRONOUSLY); | |
return (value != null ? Boolean.valueOf(value).booleanValue() : DIRECTIVE_CREATE_ASYNCHRONOUSLY_DEFAULT); | |
} | |
/** | |
* Shortcut for finding the boolean value for {@link #DIRECTIVE_TIMEOUT} | |
* directive using the given headers. | |
* | |
* Assumes the headers belong to a Spring powered bundle. Returns the | |
* timeout (in seconds) for which the application context should wait to | |
* have its dependencies satisfied. | |
* | |
* @param headers | |
* @return | |
*/ | |
public static long getTimeOut(Dictionary headers) { | |
String value = getDirectiveValue(headers, DIRECTIVE_TIMEOUT); | |
if (value != null) { | |
if (DIRECTIVE_TIMEOUT_VALUE_NONE.equalsIgnoreCase(value)) { | |
return DIRECTIVE_NO_TIMEOUT; | |
} | |
return Long.valueOf(value).longValue(); | |
} | |
return DIRECTIVE_TIMEOUT_DEFAULT; | |
} | |
/** | |
* Shortcut for finding the boolean value for | |
* {@link #DIRECTIVE_WAIT_FOR_DEPS} directive using the given headers. | |
* Assumes the headers belong to a Spring powered bundle. | |
* | |
* @param headers | |
* @return | |
*/ | |
public static boolean getWaitForDependencies(Dictionary headers) { | |
String value = getDirectiveValue(headers, DIRECTIVE_WAIT_FOR_DEPS); | |
return (value != null ? Boolean.valueOf(value).booleanValue() : DIRECTIVE_WAIT_FOR_DEPS_DEFAULT); | |
} | |
/** | |
* Returns the location headers (if any) specified by the Spring-Context | |
* header (if available). The returned Strings can be sent to a | |
* {@link org.springframework.core.io.ResourceLoader} for loading the | |
* configurations. | |
* | |
* @param headers bundle headers | |
* @return array of locations specified (if any) | |
*/ | |
public static String[] getSpringDmHeaderLocations(Dictionary headers) { | |
return getSpringDmLocationsFromHeader(getSpringContextHeader(headers)); | |
} | |
/** | |
* Similar to {@link #getSpringDmHeaderLocations(Dictionary)} but looks at a | |
* specified header directly. | |
* | |
* @param header header to look at | |
*/ | |
public static String[] getSpringDmLocationsFromHeader(String header) { | |
if (header == null) { | |
return null; | |
} | |
String[] ctxEntries; | |
if (StringUtils.hasText(header) && !(';' == header.charAt(0))) { | |
// get the config locations | |
String locations = StringUtils.tokenizeToStringArray(header, DIRECTIVE_SEPARATOR)[0]; | |
// parse it into individual token | |
ctxEntries = StringUtils.tokenizeToStringArray(locations, CONTEXT_LOCATION_SEPARATOR); | |
// replace * with a 'digestable' location | |
for (int i = 0; i < ctxEntries.length; i++) { | |
if (CONFIG_WILDCARD.equals(ctxEntries[i])) | |
ctxEntries[i] = LEGACY_SPRING_DM_CONTEXT_LOCATION; | |
} | |
} | |
else { | |
ctxEntries = new String[0]; | |
} | |
return ctxEntries; | |
} | |
} |