/* | |
* 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; | |
} | |
} |