blob: c640683abe02798e3b862f526a4c985e9cf08856 [file] [log] [blame]
/*
* 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.struts2.osgi.host;
import com.opensymphony.xwork2.config.ConfigurationException;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.struts2.osgi.OsgiUtil;
import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.main.AutoProcessor;
import org.apache.felix.main.Main;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import javax.servlet.ServletContext;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Apache felix implementation of an OsgiHost
* See http://felix.apache.org/site/apache-felix-framework-launching-and-embedding.html
* <br>
* Servlet config params:
* <p>struts.osgi.clearBundleCache: Defaults to "true" delete installed bundles when the container starts</p>
* <p>struts.osgi.logLevel: Defaults to "1". Felix log level. 1 = error, 2 = warning, 3 = information, and 4 = debug </p>
* <p>struts.osgi.runLevel: Defaults to "3". Run level to start the container.</p>
* <p>struts.osgi.felixCacheLocking: Defaults to "true". Set to true to enable Felix cache locking, false to disable it.</p>
* <p>struts.osgi.searchForPropertiesFilesInRelativePath: Defaults to "false". Set to "true" for fallback search for properties files in relative path (e.g. for unit testing).</p>
* <p>struts.osgi.felixPropertiesPath: Defaults to "default.properties". Path to Felix properties (prefix "/" will be prepended).</p>
* <p>struts.osgi.strutsOSGiPropertiesPath: Defaults to "struts-osgi.properties". Path to Struts OSGi properties (prefix "/" will be prepended).</p>
*/
public class FelixOsgiHost extends BaseOsgiHost {
private static final Logger LOG = LogManager.getLogger(FelixOsgiHost.class);
protected static final String FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION = "${jre-${felix.detect.java.specification.version}}";
protected static final String FELIX_JRE_DETECT_JPMS = "${jre-${felix.detect.jpms}}";
protected static final String FELIX_DETECT_JAVA_VERSION = "${felix.detect.java.version}";
protected static final String FELIX_JRE_BASE_PREFIX = "${jre-base";
protected static final String FELIX_JRE_BASE_SUFFIX = "}";
protected static final String OSGI_FRAMEWORK_SYSTEM_CAPABILITIES = "org.osgi.framework.system.capabilities";
protected static final String OSGI_FRAMEWORK_EXECUTIONENVIRONMENT = "org.osgi.framework.executionenvironment";
protected static final String FELIX_SERVICE_CAPS_KEY = "felix.service.caps";
protected static final String FELIX_SERVICE_CAPS = "${" + FELIX_SERVICE_CAPS_KEY + "}";
protected static final String FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION = "${eecap-${java.specification.version}}";
protected static final String FELIX_EE_CAP_DETECT_JPMS = "${eecap-${felix.detect.jpms}}";
protected static final String FELIX_EE_JAVA_SPECIFICATION_VERSION = "${ee-${java.specification.version}}";
protected static final String FELIX_EE_FELIX_DETECT_JPMS = "${ee-${felix.detect.jpms}}";
protected Felix felix;
protected void startFelix() {
// Retrieve Felix properties path from ServletContext (default to "default.properties" if not present).
String felixPropertiesPath = getServletContextParam("struts.osgi.felixPropertiesPath", "default.properties");
felixPropertiesPath = felixPropertiesPath.trim();
if (felixPropertiesPath.startsWith("/")) {
felixPropertiesPath = felixPropertiesPath.substring(1); // Must strip leading "/" if present.
}
// Load properties from felix embedded file or value specified in servlet init parameter "struts.osgi.felixPropertiesPath".
final Properties configProps = getProperties(felixPropertiesPath);
// Copy framework properties from the system properties.
Main.copySystemProperties(configProps);
replaceSystemPackages(configProps);
LOG.debug("Felix framework system capabilities: [{}]", configProps.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES));
LOG.debug("Felix framework execution environment: [{}]", configProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT));
LOG.debug("Felix service caps: [{}]", configProps.getProperty(FELIX_SERVICE_CAPS_KEY));
replaceFelixSystemPackages(configProps);
replaceFelixFrameworkSystemCapabilities(configProps);
replaceFelixExecutionEnvironment(configProps);
// Retrieve Struts OSGi properties path from ServletContext (default to "struts-osgi.properties" if not present).
String strutsOSGiPropertiesPath = getServletContextParam("struts.osgi.strutsOSGiPropertiesPath", "struts-osgi.properties");
strutsOSGiPropertiesPath = strutsOSGiPropertiesPath.trim();
if (strutsOSGiPropertiesPath.startsWith("/")) {
strutsOSGiPropertiesPath = strutsOSGiPropertiesPath.substring(1); // Must strip leading "/" if present.
}
LOG.trace("FelixOSGiHost: Before addExportedPackages");
//struts, xwork and felix exported packages
final Properties strutsConfigProps = getProperties(strutsOSGiPropertiesPath);
try {
addExportedPackages(strutsConfigProps, configProps);
} catch (Throwable t) {
throw new ConfigurationException("FelixOSGiHost couldn't start Apache Felix", t);
}
LOG.trace("FelixOSGiHost: After addExportedPackages, before addAutoStartBundles");
//find bundles and adde em to autostart property
try {
addAutoStartBundles(configProps);
} catch (Throwable t) {
throw new ConfigurationException("FelixOSGiHost couldn't start Apache Felix", t);
}
LOG.trace("FelixOSGiHost: After addAutoStartBundles. before bundle cache processing");
// Bundle cache
String storageDir = configProps.getProperty(Constants.FRAMEWORK_STORAGE);
if (storageDir == null || storageDir.isEmpty()) {
String javaTmpDir = System.getProperty("java.io.tmpdir");
if (javaTmpDir == null || javaTmpDir.isEmpty()) {
LOG.warn("Felix environment 'java.io.tmpdir': [{}], and 'org.osgi.framework.storage': [{}]. Felix bundle cache will be created at the root directory (probable failure)", javaTmpDir, storageDir);
javaTmpDir = File.separator;
}
if (javaTmpDir.endsWith(File.separator)) {
storageDir = javaTmpDir + ".felix-cache";
} else {
storageDir = javaTmpDir + File.separator + ".felix-cache";
}
configProps.setProperty(Constants.FRAMEWORK_STORAGE, storageDir);
}
LOG.debug("Storing bundles at [{}]", storageDir);
String cleanBundleCache = getServletContextParam("struts.osgi.clearBundleCache", "true");
if ("true".equalsIgnoreCase(cleanBundleCache)) {
LOG.debug("Clearing bundle cache");
configProps.put(FelixConstants.FRAMEWORK_STORAGE_CLEAN, FelixConstants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
}
String cacheLocking = getServletContextParam("struts.osgi.felixCacheLocking", "true");
configProps.put("felix.cache.locking", cacheLocking);
LOG.debug("Felix bundle cache locking: [{}]", cacheLocking);
LOG.trace("FelixOSGiHost: After bundle cache processing, before configProps and init");
//other properties
configProps.put(FelixConstants.SERVICE_URLHANDLERS_PROP, "false");
configProps.put(FelixConstants.LOG_LEVEL_PROP, getServletContextParam("struts.osgi.logLevel", "1"));
configProps.put(FelixConstants.BUNDLE_CLASSPATH, ".");
configProps.put(FelixConstants.FRAMEWORK_BEGINNING_STARTLEVEL, getServletContextParam("struts.osgi.runLevel", "3"));
try {
felix = new Felix(configProps);
LOG.trace("FelixOSGiHost: After Felix construct. before init");
felix.init();
LOG.trace("FelixOSGiHost: After Felix init. before AutoProcessor process");
AutoProcessor.process(configProps, felix.getBundleContext());
LOG.trace("FelixOSGiHost: After Felix AutoProcessor process, before start");
felix.start();
LOG.trace("FelixOSGiHost: After Felix start");
// Start detailed service/bundle state for debugging.
if (LOG.isDebugEnabled()) {
ServiceReference[] serviceReferences = felix.getRegisteredServices();
LOG.debug("Felix registered service references: [{}]", Arrays.toString(serviceReferences));
serviceReferences = felix.getServicesInUse();
LOG.debug("Felix in-use service references: [{}]", Arrays.toString(serviceReferences));
Map<String, Bundle> bundleMap = this.getBundles();
LOG.debug("Felix bundle map has size: [{}]", bundleMap.size());
Set<String> bundleKeys = bundleMap.keySet();
Iterator<String> keyIterator = bundleKeys.iterator();
while (keyIterator.hasNext()) {
String currentKey = keyIterator.next();
Bundle currentBundle = bundleMap.get(currentKey);
if (currentBundle != null) {
ServiceReference[] bundleRegisteredServices = currentBundle.getRegisteredServices();
ServiceReference[] bundleServicesInUse = currentBundle.getServicesInUse();
LOG.debug(" Key: [{}], SymbolicName: [{}], Location: [{}], BundleID: [{}], State: [{}]",
currentKey, currentBundle.getSymbolicName(), currentBundle.getLocation(), currentBundle.getBundleId(), currentBundle.getState());
LOG.debug(" Bundle Registered Services: [{}]", Arrays.toString(bundleRegisteredServices));
LOG.debug(" Bundle Services In Use: [{}]", Arrays.toString(bundleServicesInUse));
} else {
LOG.debug(" Key: [{}] returned a null bundle", currentKey);
}
}
Bundle[] bundles = this.getBundleContext().getBundles();
if (bundles != null) {
LOG.debug("BundleContext bundle array has size: [{}]", bundles.length);
for (int index = 0; index < bundles.length; index++) {
Bundle currentBundle = bundles[index];
if (currentBundle != null) {
LOG.debug(" Bundle [{}], SymbolicName: [{}], Location: [{}], BundleID: [{}], State: [{}]",
index, currentBundle.getSymbolicName(), currentBundle.getLocation(), currentBundle.getBundleId(), currentBundle.getState());
} else {
LOG.debug(" Bundle [{}] is null", index);
}
}
} else {
LOG.debug("Default Bundle Context bundle array is null");
}
}
// End detailed service/bundle state for debugging.
LOG.trace("Apache Felix is running");
}
catch (Throwable t) {
throw new ConfigurationException("FelixOSGiHost couldn't start Apache Felix", t);
}
addSpringOSGiSupport();
//add the bundle context to the ServletContext
servletContext.setAttribute(OSGI_BUNDLE_CONTEXT, felix.getBundleContext());
}
@Override
public void init(ServletContext servletContext) {
this.servletContext = servletContext;
startFelix();
}
@Override
public Map<String, Bundle> getBundles() {
if (felix == null) {
throw new IllegalStateException("Felix reference is null (never started or initialized)"); // Clearer than a NPE.
}
Map<String, Bundle> bundles = new HashMap<>();
for (Bundle bundle : felix.getBundleContext().getBundles()) {
bundles.put(bundle.getSymbolicName(), bundle);
}
return Collections.unmodifiableMap(bundles);
}
@Override
public Map<String, Bundle> getActiveBundles() {
if (felix == null) {
throw new IllegalStateException("Felix reference is null (never started or initialized)"); // Clearer than a NPE.
}
Map<String, Bundle> bundles = new HashMap<>();
for (Bundle bundle : felix.getBundleContext().getBundles()) {
if (bundle.getState() == Bundle.ACTIVE) {
bundles.put(bundle.getSymbolicName(), bundle);
}
}
return Collections.unmodifiableMap(bundles);
}
@Override
public BundleContext getBundleContext() {
if (felix == null) {
throw new IllegalStateException("Felix reference is null (never started or initialized)"); // Clearer than a NPE.
}
return felix.getBundleContext();
}
@Override
public void destroy() throws Exception {
if (felix == null) {
throw new IllegalStateException("Felix reference is null (never started or initialized)"); // Clearer than a NPE.
}
try {
felix.stop();
LOG.trace("Apache Felix has stopped");
}
catch (Throwable t) {
LOG.error("FelixOSGiHost stop failure", t);
throw t; // Re-throw.
}
}
@Override
protected void addSpringOSGiSupport() {
// see the javadoc for org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext for more details
// OsgiBundleXmlWebApplicationContext expects the the BundleContext to be set in the ServletContext under the attribute
// OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE
try {
Class clazz = Class.forName("org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext");
String key = (String) clazz.getDeclaredField("BUNDLE_CONTEXT_ATTRIBUTE").get(null);
if (felix == null) {
throw new IllegalStateException("Felix reference is null (never started or initialized)"); // Clearer than a NPE.
}
servletContext.setAttribute(key, felix.getBundleContext());
} catch (ClassNotFoundException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Spring OSGi support is not enabled");
}
} catch (Exception e) {
LOG.error("The API of Spring OSGi has changed and the field [{}] is no longer available. The OSGi plugin needs to be updated",
"org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE", e);
}
}
/**
* Replace all instances of {@link FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION} and {@link FELIX_JRE_DETECT_JPMS},
* within the {@link Constants.FRAMEWORK_SYSTEMPACKAGES} property of the provided properties. The replacement will be the value
* for the key "jre-x.y" in the properties parameter (where x.y is the JRE version after transforming the System "java.version" property.
* For example: "jre-x.y" is "jre-1.8" for Java 8, "jre-9.0" for Java 9, and "jre-11.0" for Java 11.
*
* While performing the replacement, the elements within jre-x.y will also undergo a replacement of the substring {@link FELIX_DETECT_JAVA_VERSION}
* into something like "0.0.0.JavaSE_001_008" for Java 8 and earlier, "0.0.0.JavaSE_009" for Java 9 and newer. If you prefer
* manual control, use literal strings rather than {@link FELIX_DETECT_JAVA_VERSION} in your properties file.
*
* @param properties OSGi properties for which the {@link Constants.FRAMEWORK_SYSTEMPACKAGES} property's values
* substrings {@link FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION} and {@link FELIX_JRE_DETECT_JPMS} will be replaced by the value for the key
* "jre-xxx" (where xxx is the JRE version).
* If no {@link Constants.FRAMEWORK_SYSTEMPACKAGES} property exists, this is a no-op.
*/
protected void replaceFelixSystemPackages(Properties properties) {
//Felix has a way to load the config file and substitution expressions
//but the method does not have a way to specify the file (other than in an env variable)
if (properties == null) {
throw new IllegalArgumentException("Cannot replace Felix system packages given a null properties reference");
}
//${jre-${java.specification.version}}
String systemPackages = (String) properties.get(Constants.FRAMEWORK_SYSTEMPACKAGES);
if (systemPackages != null && !systemPackages.isEmpty()) {
LOG.debug("OSGi System Packages (before replacement): [{}]", systemPackages);
final String systemJavaVersion = System.getProperty("java.version");
if (systemJavaVersion != null && !systemJavaVersion.isEmpty()) {
final String jreJavaSpecificationVersion = "jre-" + OsgiUtil.generateJavaVersionForSystemPackages(systemJavaVersion);
LOG.debug(" System java.version: [{}], generated Java Specification Version: [{}]", systemJavaVersion, jreJavaSpecificationVersion);
String jreJavaSpecificationVersionSubstitution = (String) properties.get(jreJavaSpecificationVersion);
if (jreJavaSpecificationVersionSubstitution != null && !jreJavaSpecificationVersionSubstitution.isEmpty()) {
//${detect.java.version}
jreJavaSpecificationVersionSubstitution = expandAllFelixJREBaseElements(jreJavaSpecificationVersionSubstitution, properties);
final boolean constainsFelix_JRE_Java_Specification_Version = systemPackages.contains(FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION);
final boolean constainsFelix_JRE_JPMS = systemPackages.contains(FELIX_JRE_DETECT_JPMS);
jreJavaSpecificationVersionSubstitution = jreJavaSpecificationVersionSubstitution.replace(FELIX_DETECT_JAVA_VERSION, OsgiUtil.generateJava_SE_SystemPackageVersionString(systemJavaVersion)).trim();
if (constainsFelix_JRE_Java_Specification_Version && constainsFelix_JRE_JPMS) {
systemPackages = systemPackages.replace(FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION, jreJavaSpecificationVersionSubstitution);
systemPackages = systemPackages.replace(FELIX_JRE_DETECT_JPMS, "").trim(); // Only replace one instance when both set (avoid duplicates).
} else if (constainsFelix_JRE_Java_Specification_Version) {
systemPackages = systemPackages.replace(FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION, jreJavaSpecificationVersionSubstitution);
} else if (constainsFelix_JRE_JPMS) {
systemPackages = systemPackages.replace(FELIX_JRE_DETECT_JPMS, jreJavaSpecificationVersionSubstitution);
} else {
LOG.warn("Properties property [{}] is present, but we are unable to replace Felix JRE system packages due to missing at least one of [{}] [{}] keys in the configuration.",
jreJavaSpecificationVersion, FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION, FELIX_JRE_DETECT_JPMS);
}
properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages);
} else {
LOG.warn("Properties property [{}] is null or empty. Unable to replace Felix JRE system packages. Felix JRE system packages will be cleared.", jreJavaSpecificationVersion);
systemPackages = systemPackages.replace(FELIX_JRE_DETECT_JAVA_SPECIFICATION_VERSION, "");
systemPackages = systemPackages.replace(FELIX_JRE_DETECT_JPMS, "").trim();
properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages);
}
} else {
LOG.warn("System property [{}] is null or empty. Unable to replace Felix JRE system packages.", "java.version");
}
LOG.debug("OSGi System Packages (after replacement): [{}]", systemPackages);
} else {
LOG.warn("Unable to replace Felix JRE system packages. Properties required key [{}] is missing or empty.",
Constants.FRAMEWORK_SYSTEMPACKAGES);
}
}
/**
* Replace all instances of the {@link FELIX_SERVICE_CAPS}, {@link FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION} and {@link FELIX_EE_CAP_DETECT_JPMS}
* within the {@link Constants.FRAMEWORK_SYSTEMCAPABILITIES} property of the provided properties. The replacements will be based on the lookups within
* the properties for the keys that match the substitution strings for each element (i.e. the element without "${}", transformed for JRE version if required).
*
* @param properties OSGi properties for which the {@link Constants.FRAMEWORK_SYSTEMCAPABILITIES} property's values substrings
* {@link FELIX_SERVICE_CAPS}, {@link FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION} and {@link FELIX_EE_CAP_DETECT_JPMS} will be replaced by the
* value for the keys that match the substitution strings for each element (i.e. the element without "${}", transformed for JRE version if required).
* If no {@link Constants.FRAMEWORK_SYSTEMCAPABILITIES} property exists, this is a no-op.
*/
protected void replaceFelixFrameworkSystemCapabilities(Properties properties) {
if (properties == null) {
throw new IllegalArgumentException("Cannot replace Felix system capabilities given a null properties reference");
}
String systemCapabilities = (String) properties.get(Constants.FRAMEWORK_SYSTEMCAPABILITIES);
if (systemCapabilities != null && !systemCapabilities.isEmpty()) {
LOG.debug("OSGi System Capabilities (before replacement): [{}]", systemCapabilities);
systemCapabilities = expandAllOsgiFrameworkSystemCapabilities(systemCapabilities, properties);
properties.put(Constants.FRAMEWORK_SYSTEMCAPABILITIES, systemCapabilities);
LOG.debug("OSGi System Capabilities (after replacement): [{}]", systemCapabilities);
} else {
LOG.warn("Unable to replace Felix system capabilities. Properties required key [{}] is missing or empty",
Constants.FRAMEWORK_SYSTEMCAPABILITIES);
}
}
/**
* Replace all instances of the {@link FELIX_EE_JAVA_SPECIFICATION_VERSION} and {@link FELIX_EE_FELIX_DETECT_JPMS}
* within the {@link Constants.FRAMEWORK_EXECUTIONENVIRONMENT} property of the provided properties. The replacements will be based on the lookups within
* the properties for the keys that match the substitution strings for each element (i.e. the element without "${}", transformed for JRE version if required).
*
* @param properties OSGi properties for which the {@link Constants.FRAMEWORK_EXECUTIONENVIRONMENT} property's values substrings
* {@link FELIX_EE_JAVA_SPECIFICATION_VERSION} and {@link FELIX_EE_FELIX_DETECT_JPMS} will be replaced by the value
* for the keys that match the substitution strings for each element (i.e. the element without "${}", transformed for JRE version if required).
* If no {@link Constants.FRAMEWORK_EXECUTIONENVIRONMENT} property exists, this is a no-op.
*/
protected void replaceFelixExecutionEnvironment(Properties properties) {
if (properties == null) {
throw new IllegalArgumentException("Cannot replace Felix execution environment given a null properties reference");
}
String executionEnvironment = (String) properties.get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
if (executionEnvironment != null && !executionEnvironment.isEmpty()) {
LOG.debug("OSGi Execution Environment (before replacement): [{}]", executionEnvironment);
executionEnvironment = expandAllOsgiFrameworkExecutionEnvironments(executionEnvironment, properties);
properties.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, executionEnvironment);
LOG.debug("OSGi Execution Environment (after replacement): [{}]", executionEnvironment);
} else {
LOG.warn("Unable to replace Felix execution environment. Properties required key [{}] is missing or empty",
Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
}
}
/**
* Recursive method to expand all Felix JRE base elements from the configuration. Once complete the resulting string
* should be the expansion of all ${jre-base} elements into a single string incorporating all the base elements.
*
* @param jreProperty A string retrieval of a JRE property entry (e.g. jre-1.7), that may or may not contain JRE base elements (e.g. ${jre-base-1.7}).
* @param properties The Felix properties.
* @return A fully-expanded version of jreProperty, after recursively expanding all jre-base elements in the Felix properties.
*/
protected String expandAllFelixJREBaseElements(String jreProperty, Properties properties) {
String result = jreProperty;
if (properties == null) {
throw new IllegalArgumentException("Cannot expand Felix JRE base elements given a null properties reference");
}
if (jreProperty != null && !jreProperty.isEmpty()) {
final int nextBaseStart = jreProperty.indexOf(FELIX_JRE_BASE_PREFIX);
final int nextBaseEnd = (nextBaseStart != -1 ? jreProperty.indexOf(FELIX_JRE_BASE_SUFFIX, nextBaseStart + FELIX_JRE_BASE_PREFIX.length() ) : -1);
if (nextBaseStart != -1 && nextBaseEnd != -1) {
final String jreBaseReplace = jreProperty.substring(nextBaseStart, nextBaseEnd + 1); // Full token including "${}".
final String jreBaseLookup = jreBaseReplace.substring(2, jreBaseReplace.length() - 1); // Drop surrounding "${}" for lookup.
String jreBaseSubstitution = (String) properties.get(jreBaseLookup);
LOG.trace(" JRE Base elements - Result so far [{}], current replace[{}], current lookup [{}], current substitution [{}]", jreProperty, jreBaseReplace, jreBaseLookup, jreBaseSubstitution);
if (jreBaseSubstitution == null || jreBaseSubstitution.isEmpty()) {
LOG.warn("Unable to expand Felix JRE base property [{}] as it is missing or empty. Replacing with empty string", jreBaseLookup);
jreBaseSubstitution = "";
} else {
jreBaseSubstitution = jreBaseSubstitution.trim();
}
result = expandAllFelixJREBaseElements(jreProperty.replace(jreBaseReplace, jreBaseSubstitution), properties); // Recurse until all possible Felix JRE base elements have been replaced.
}
} else {
LOG.warn("Unable to expand Felix JRE base elements. Base property [{}] is missing or empty", jreProperty);
}
return result;
}
/**
* Method to expand all OSGi framework system capabilities elements from the configuration. Once complete the resulting string
* should be the expansion of all entries found in the {@link Constants.FRAMEWORK_SYSTEMCAPABILITIES} content passed into the method.
*
* @param capabilities A string retrieval of an OSGi framework system capabilities property entry {@link Constants.FRAMEWORK_SYSTEMCAPABILITIES} containing one or more of
* {@link FELIX_SERVICE_CAPS} {@link FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION} {@link FELIX_EE_CAP_DETECT_JPMS}.
* @param properties The Felix properties.
* @return A fully-expanded version of the {@link Constants.FRAMEWORK_SYSTEMCAPABILITIES} content, after expanding all its contained elements in the Felix properties.
*/
protected String expandAllOsgiFrameworkSystemCapabilities(String capabilities, Properties properties) {
String result = capabilities;
if (properties == null) {
throw new IllegalArgumentException("Cannot expand OSGi framework system capabilities elements given a null properties reference");
}
if (capabilities != null && !capabilities.isEmpty()) {
final String systemJavaVersion = System.getProperty("java.version");
String eeCapJavaSpecificationVersion = null;
String eeCapJavaSpecificationVersionSubstitution = null;
final boolean constains_Felix_Service_Caps = capabilities.contains(FELIX_SERVICE_CAPS);
final boolean constains_Felix_EE_Cap_Java_Specification_Version = capabilities.contains(FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION);
final boolean constains_Felix_EE_Cap_Detect_JPMS = capabilities.contains(FELIX_EE_CAP_DETECT_JPMS);
if (systemJavaVersion != null && !systemJavaVersion.isEmpty()) {
eeCapJavaSpecificationVersion = "eecap-" + OsgiUtil.generateJavaVersionForSystemPackages(systemJavaVersion);
LOG.debug(" System java.version: [{}], generated EE Capabilities Java Specification Version: [{}]", systemJavaVersion, eeCapJavaSpecificationVersion);
eeCapJavaSpecificationVersionSubstitution = properties.getProperty(eeCapJavaSpecificationVersion);
if (eeCapJavaSpecificationVersionSubstitution == null || eeCapJavaSpecificationVersionSubstitution.isEmpty()) {
LOG.warn("System property [{}] is null or empty. Unable to replace Felix EE capabilities. Replacing with empty string", eeCapJavaSpecificationVersion);
eeCapJavaSpecificationVersionSubstitution = "";
} else {
eeCapJavaSpecificationVersionSubstitution = eeCapJavaSpecificationVersionSubstitution.trim();
}
} else {
LOG.warn("System property [{}] is null or empty. Unable to replace Felix EE capabilities.", "java.version");
}
if (constains_Felix_Service_Caps) {
String felixServiceCapsSubstitution = properties.getProperty(FELIX_SERVICE_CAPS_KEY);
if (felixServiceCapsSubstitution == null || felixServiceCapsSubstitution.isEmpty()) {
LOG.warn("System property [{}] is null or empty. Unable to replace Felix service capabilities. Replacing with empty string", FELIX_SERVICE_CAPS_KEY);
felixServiceCapsSubstitution = "";
} else {
felixServiceCapsSubstitution = felixServiceCapsSubstitution.trim();
}
result = result.replace(FELIX_SERVICE_CAPS, felixServiceCapsSubstitution);
}
if (constains_Felix_EE_Cap_Java_Specification_Version && constains_Felix_EE_Cap_Detect_JPMS) {
result = result.replace(FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION, eeCapJavaSpecificationVersionSubstitution);
result = result.replace(FELIX_EE_CAP_DETECT_JPMS, "").trim(); // Only replace one instance when both set (avoid duplicates).
} else if (constains_Felix_EE_Cap_Java_Specification_Version) {
result = result.replace(FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION, eeCapJavaSpecificationVersionSubstitution);
} else if (constains_Felix_EE_Cap_Detect_JPMS) {
result = result.replace(FELIX_EE_CAP_DETECT_JPMS, eeCapJavaSpecificationVersionSubstitution);
} else {
LOG.warn("Properties property [{}] is present, but we are unable to replace Felix EE capabilities due to missing at least one of [{}] [{}] keys in the configuration",
eeCapJavaSpecificationVersion, FELIX_EE_CAP_JAVA_SPECIFICATION_VERSION, FELIX_EE_CAP_DETECT_JPMS);
}
} else {
LOG.warn("Unable to expand OSGi framework system capabilities elements. Capability parameter [{}] is missing or empty", capabilities);
}
return result;
}
/**
* Method to expand all OSGi framework execution environment elements from the configuration. Once complete the resulting string
* should be the expansion of all entries found in the {@link Constants.FRAMEWORK_EXECUTIONENVIRONMENT} content passed into the method.
*
* @param executionEnvironment A string retrieval of an OSGi framework execution environment property entry {@link Constants.FRAMEWORK_EXECUTIONENVIRONMENT} containing one or more of
* {@link FELIX_EE_JAVA_SPECIFICATION_VERSION} {@link FELIX_EE_FELIX_DETECT_JPMS}.
* @param properties The Felix properties.
* @return A fully-expanded version of the {@link Constants.FRAMEWORK_EXECUTIONENVIRONMENT} content, after expanding all its contained elements in the Felix properties.
*/
protected String expandAllOsgiFrameworkExecutionEnvironments(String executionEnvironment, Properties properties) {
String result = executionEnvironment;
if (properties == null) {
throw new IllegalArgumentException("Cannot expand OSGi framework execution environment elements given a null properties reference");
}
if (executionEnvironment != null && !executionEnvironment.isEmpty()) {
final String systemJavaVersion = System.getProperty("java.version");
String eeJavaSpecificationVersion = null;
String eeJavaSpecificationVersionSubstitution = null;
final boolean constains_Felix_EE_Java_Specification_Version = executionEnvironment.contains(FELIX_EE_JAVA_SPECIFICATION_VERSION);
final boolean constains_Felix_EE_Felix_Detect_JPMS = executionEnvironment.contains(FELIX_EE_FELIX_DETECT_JPMS);
if (systemJavaVersion != null && !systemJavaVersion.isEmpty()) {
eeJavaSpecificationVersion = "ee-" + OsgiUtil.generateJavaVersionForSystemPackages(systemJavaVersion);
LOG.debug(" System java.version: [{}], generated EE Java Specification Version: [{}]", systemJavaVersion, eeJavaSpecificationVersion);
eeJavaSpecificationVersionSubstitution = properties.getProperty(eeJavaSpecificationVersion);
if (eeJavaSpecificationVersionSubstitution == null || eeJavaSpecificationVersionSubstitution.isEmpty()) {
LOG.warn("System property [{}] is null or empty. Unable to replace Felix EE execution environment. Replacing with empty string", eeJavaSpecificationVersion);
eeJavaSpecificationVersionSubstitution = "";
} else {
eeJavaSpecificationVersionSubstitution = eeJavaSpecificationVersionSubstitution.trim();
}
} else {
LOG.warn("System property [{}] is null or empty. Unable to replace Felix EE execution environment", "java.version");
}
if (constains_Felix_EE_Java_Specification_Version && constains_Felix_EE_Felix_Detect_JPMS) {
result = result.replace(FELIX_EE_JAVA_SPECIFICATION_VERSION, eeJavaSpecificationVersionSubstitution);
result = result.replace(FELIX_EE_FELIX_DETECT_JPMS, "").trim(); // Only replace one instance when both set (avoid duplicates).
} else if (constains_Felix_EE_Java_Specification_Version) {
result = result.replace(FELIX_EE_JAVA_SPECIFICATION_VERSION, eeJavaSpecificationVersionSubstitution);
} else if (constains_Felix_EE_Felix_Detect_JPMS) {
result = result.replace(FELIX_EE_FELIX_DETECT_JPMS, eeJavaSpecificationVersionSubstitution);
} else {
LOG.warn("Properties property [{}] is present, but we are unable to replace Felix EE execution environment due to missing at least one of [{}] [{}] keys in the configuration",
eeJavaSpecificationVersion, FELIX_EE_JAVA_SPECIFICATION_VERSION, FELIX_EE_FELIX_DETECT_JPMS);
}
} else {
LOG.warn("Unable to expand OSGi framework execution environment elements. Execution environment parameter [{}] is missing or empty", executionEnvironment);
}
return result;
}
}