Compare commits

..

47 Commits

Author SHA1 Message Date
Lukasz Lenart b3a9d82d58 [maven-release-plugin] prepare release STRUTS_2_5_25 2020-09-17 13:36:38 +02:00
Lukasz Lenart 828d02fcbd Merge pull request #438 from yasserzamani/WW-5087
WW-5087 handle Parameter.Empty properly
2020-09-15 07:48:19 +02:00
Yasser Zamani 88b05cdaed WW-5087 handle Parameter.Empty properly
There was a bug with AliasInterceptor not handling the Parameter.Empty that is returned from HttpParameters.get(). Since HttpParameters.get() always returns a non-null value, the Evaluated object is treated as always being defined, which results in the empty value being set incorrectly on the stack.
2020-09-14 18:00:54 +04:30
Lukasz Lenart e60df05c27 Merge pull request #437 from gregh3269/struts-2-5-x
Empty file upload, storeLocation null gives wrong error message.
2020-09-09 12:57:59 +02:00
Greg Huber 1efcc938cf Empty file upload, storeLocation null gives wrong error message. 2020-09-09 11:02:55 +01:00
Lukasz Lenart 7b1c3b0fca Merge pull request #436 from JCgH4164838Gh792C124B5/WW-5075_S2_5_OSGiPluginBackportfix
Backport a few PR#433 OSGi plugin-related changes from 2.6 to 2.5.
2020-09-09 07:52:57 +02:00
JCgH4164838Gh792C124B5 10a834a54c Backport a few PR#433 OSGi plugin-related changes from 2.6 to 2.5.
- Fix to allow the OSGi plugin to initialize with no internet connection.
- Fix an unintended JS change for the admin bundle.
- Fix the OSGi Demo bundle so it works under 2.5.x as well.
2020-09-08 15:25:02 -04:00
Lukasz Lenart abb1ac3c99 [maven-release-plugin] prepare for next development iteration 2020-08-18 07:38:38 +02:00
Lukasz Lenart dcc59937a8 [maven-release-plugin] prepare release STRUTS_2_5_24 2020-08-18 07:38:25 +02:00
Lukasz Lenart 22c88faf38 WW-5086 Fixes s:set tag to persists empty body 2020-08-17 09:46:27 +02:00
Lukasz Lenart 1191ca3ed0 WW-5086 Fixes s:set tag with empty body 2020-08-17 08:30:56 +02:00
Yasser Zamani bcfb769005 Merge pull request #427 from JCgH4164838Gh792C124B5/WW-5075_S2_5_OSGiPluginUpdate
WW-5075 Fixes for OSGi plugin in 2.5.x
2020-07-29 08:08:57 +04:30
JCgH4164838Gh792C124B5 d61be3e4ce Updated with suggested changes from review.
- BundlePackageLoader changes suggested by L. Lenart.
- FelixOsgiHost changes to better handle Felix bundle cache location
  processing (Windows and Linux).
- FelixOsgIHost test modified to not fail if felix-cache directory cannot
  be created (warn only) to avoid failing the whole build.
2020-07-25 16:11:02 -04:00
JCgH4164838Gh792C124B5 c7149ab1bc Initial attempt to fix OSGi Plugin issues discovered during examination of
WW-5075.
- Indication is that the OSGi Plugin has been failing since Struts 2.3.4.
- Applied fix to ResourceFinder to restore 2.3.x logic that was broken in
  2.5.x.
- Implemented changes to restore OSGi Plugin functionality for 2.5.x.
- Applied changes to the OSGi Admin Bundle to function under 2.5.x.
- Updated the OSGi Admin Bundle JS libraries and related CSS to newer
  versions.
- Added new unit tests for the OSGi plugin.
- The OSGi plugin will only function properly with exploded/expanded WAR
  files.
2020-07-20 01:12:12 -04:00
Yasser Zamani 4504e610f1 Merge pull request #425 from apache/WW-5077-dmi-aware
[WW-5077] DMI aware pattern
2020-07-17 14:16:36 +04:30
Lukasz Lenart 9b3a23eb28 WW-5077 Adjusts DMI aware Regex to be more narrowed 2020-07-17 08:08:57 +02:00
Lukasz Lenart 3220d832d2 WW-5077 Restores escaped brackets 2020-07-16 07:30:06 +02:00
Lukasz Lenart bd007953ea Sets a proper timestamp as maven-jar-plugin doesn't support "now" 2020-07-11 20:39:55 +02:00
Lukasz Lenart 1b0c743e85 WW-5077 Uses a different pattern if DMI is enabled 2020-07-11 20:31:30 +02:00
Lukasz Lenart 89e02a4fb4 Adds outputTimestamp property to support reproducible build
See the link for more details
https://lists.apache.org/thread.html/r27ebcfec6431dc5f4830744e3567f082d8f140dc579f2a35408ea25c%40%3Cusers.maven.apache.org%3E
2020-07-11 19:45:33 +02:00
Lukasz Lenart 8cb93ad39a [maven-release-plugin] prepare for next development iteration 2020-07-02 07:53:53 +02:00
Lukasz Lenart e8656d5737 [maven-release-plugin] prepare release STRUTS_2_5_23 2020-07-02 07:53:39 +02:00
Lukasz Lenart 7f10ed505f Merge pull request #419 from apache/WW-5077-better-logs
[WW-5077] Better logs
2020-05-24 09:32:56 +02:00
Lukasz Lenart 043814b774 WW-5077 Uses better logging to inform user about excluded params 2020-05-24 08:59:40 +02:00
Lukasz Lenart 0fabde9f97 Merge pull request #417 from JCgH4164838Gh792C124B5/LL_tiny-improvements_unittest
Provide unit test for partial backport PR#416 for 2.5.x
2020-05-18 07:49:51 +02:00
JCgH4164838Gh792C124B5 4457f03c70 Provide unit test for partial backport of L. Lenart's PR#414 to 2.5.x:
- DispatcherTest provides code coverage tests of all getLocale() paths.
2020-05-17 14:20:41 -04:00
Lukasz Lenart 3d3512a399 Merge pull request #416 from JCgH4164838Gh792C124B5/Partial_Backport_LL_tiny-improvements
Partial backport of Tiny improvements PR#414 to 2.5.x:
2020-05-17 08:58:49 +02:00
JCgH4164838Gh792C124B5 59a6cbf6ca Partial backport of L. Lenart's PR#414 to 2.5.x:
- Improve Dispatcher getLocale() handling if running in AppEngine.
- Implemented the same improvement to the defaultLocale logic path within
  getLocale().
2020-05-16 18:48:35 -04:00
Lukasz Lenart 19802b0614 Bumps struts-master and maven-site-plugin 2020-05-14 07:21:48 +02:00
Lukasz Lenart e46e662a7a Merge pull request #411 from JCgH4164838Gh792C124B5/WW-5072_fix
Minor change to fix WW-5072
2020-05-03 12:00:20 +02:00
JCgH4164838Gh792C124B5 4786fba0d7 Minor change to fix WW-5072 (single file upload failure):
- Add action-local method to get upload file size.
- Change file upload validation to use new method.
2020-05-02 17:51:45 -04:00
Lukasz Lenart e32bd7dba9 Merge pull request #405 from apache/WW-5065-append-or-not
[WW-5065] Defines a new flag to control appending params
2020-04-26 19:51:29 +02:00
Lukasz Lenart 6e1d2add07 WW-5065 Adds missing flag value 2020-04-24 09:33:06 +02:00
Lukasz Lenart 2eeac367fe WW-5065 Cleans up code and marks old constructor as deprecated 2020-04-24 09:32:50 +02:00
Lukasz Lenart 30b43044a3 WW-5065 Defines a new flag to control appending params 2020-04-23 07:13:14 +02:00
Lukasz Lenart 5c82f0246e Merge pull request #400 from atkaiser/WW5065-FixAbstractMatcherReplaceParams
[WW-5065] Removing unnecessary part of AbstractMatcher#replaceParameters
2020-04-22 06:59:21 +02:00
Lukasz Lenart fff35cfd9d Merge pull request #402 from JCgH4164838Gh792C124B5/WW-5067-25x-Fix1
WW-5067-Fix1 (correct accidental artifactId change)
2020-04-20 07:45:58 +02:00
Lukasz Lenart 1526b36dd6 Merge pull request #404 from JCgH4164838Gh792C124B5/WW-5069
Initial fix for WW-5069 (improve build behaviour JDK9+)
2020-04-20 07:23:57 +02:00
JCgH4164838Gh792C124B5 6d6a422db7 Initial fix for WW-5069:
- Force US Locale for two tests that use date strings of the form
MM/DD/CCYY.
- When the tests/build are run in an environment with a non-US Locale
and newer JDKs the tests may fail due to inability to parse the US date
format.

Note: Was noticed on Windows 10 with JDK11, but could affect other
environments when run in a non-US Locale.
2020-04-19 21:50:09 -04:00
JCgH4164838Gh792C124B5 5cf57b9132 WW-5067-Fix1
- Accidental change of artifactId when groupId was changed for weld-se in
original PR.  This commit restores the originally intended artifactId.
2020-04-18 15:49:44 -04:00
Lukasz Lenart f4c0135878 Merge pull request #401 from JCgH4164838Gh792C124B5/WW-5067-25x
Proposed WW-5067 change
2020-04-13 09:08:26 +02:00
JCgH4164838Gh792C124B5 77cfae3084 Proposed WW-5067 change.
------
Proposed list of library version updates:
---
- cdi-api 1.0-SP4 -> 1.2
- weld-core 1.0.1-SP4 -> 2.2.16.SP1
- weld-se 1.0.1-Final -> weld-se-core 2.2.16.SP1
- slf4j-api 1.7.28 -> 1.7.30
- slf4j-simple 1.7.28 -> 1.7.30
- jackson 2.10.0 -> 2.10.3
- ognl 3.1.26 -> 3.1.28
- asm 7.1 -> 7.3.1
- spring 4.3.25.RELEASE -> 4.3.26.RELEASE
- freemarker 2.3.28 -> 2.3.30
- org.apache.felix.main 4.6.1 -> 6.0.3
---
Proposed list of Maven plugin version updates:
---
- doxia-core 1.8 -> 1.9.1
- doxia-module-markdown 1.7 -> 1.9.1
- maven-project-info-reports-plugin 2.7 -> 3.0.0
- updateimpact-maven-plugin 1.0.10 -> 1.0.12
- maven-surefire-plugin 2.22.1 -> 3.0.0-M4
- maven-war-plugin 2.1 -> 3.2.3
- maven-dependency-plugin 2.10 -> 3.1.2
- dependency-check-maven 3.3.4 -> 5.3.2
Note: Unable to upgrade maven-bundle-plugin past 2.1.0 as it introduced
      OOM during JDK7 builds with default heap settings.
---
2020-04-12 23:38:43 -04:00
Alex Kaiser ec56290056 Removing unnecessary part of AbstractMatcher#replaceParameters method and adding a test to make sure it is working correctly 2020-04-08 11:23:02 -07:00
Lukasz Lenart 47c87bc62c Merge pull request #385 from JCgH4164838Gh792C124B5/local_25x_TstFix1
Fix incorrect encoding strings in FileUploadInterceptorTest
2019-12-01 12:34:54 +01:00
JCgH4164838Gh792C124B5 ce467b7fa5 Fix incorrect encoding strings provided in FileUPloadInterceptorTest
- Tests were using "text/html" which is not a valid character encoding.
No impact currently, but the tests could fail in the future if this is not
corrected.  If the code is re-used elsewhere for a test of an action a
valid encoding will be needed.
- Switch to a predefined constant CharSet to get the string instead.
2019-11-30 16:06:05 -05:00
Lukasz Lenart 73eb6ed189 Adds a link to Struts Examples pages 2019-11-25 09:39:50 +01:00
Lukasz Lenart 80a91dc75f [maven-release-plugin] prepare for next development iteration 2019-11-17 20:17:42 +01:00
104 changed files with 4587 additions and 804 deletions
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>2.5.22</version>
<version>2.5.25</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+2 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -167,7 +167,7 @@
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
<version>5.4.3.Final</version>
</dependency>
<!-- The Servlet API mocks in Spring Framework 4.x only supports Servlet 3.0 and higher.
@@ -84,4 +84,12 @@ public class FileUploadAction extends ActionSupport {
public void setCaption(String caption) {
this.caption = caption;
}
public long getUploadSize() {
if (upload != null) {
return upload.length();
} else {
return 0;
}
}
}
@@ -26,7 +26,7 @@
<validators>
<field name="upload">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[upload.length() > 0]]></param>
<param name="expression"><![CDATA[getUploadSize() > 0]]></param>
<message>File cannot be empty</message>
</field-validator>
</field>
@@ -39,7 +39,7 @@
<div class="hero-unit">
<h1>Welcome!</h1>
<p>The Struts Showcase demonstrates a variety of use cases and tag usages. Essentially, the application exercises various framework features in isolation. The Showcase is not meant as a "best practices" example.</p>
<p>For more "by example" solutions, see the <a class="btn btn-primary btn-large">Struts Cookbook &raquo;</a> pages.</p>
<p>For more "by example" solutions, see the <a href="https://github.com/apache/struts-examples" class="btn btn-primary btn-large">Struts Examples &raquo;</a> pages.</p>
</div>
</div>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+6 -3
View File
@@ -30,7 +30,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -45,7 +45,7 @@
</licenses>
<properties>
<struts-version.version>2.5.22</struts-version.version>
<struts-version.version>2.5.25</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -181,6 +181,9 @@
</dependencyManagement>
<scm>
<tag>STRUTS_2_5_22</tag>
<tag>STRUTS_2_5_25</tag>
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
<url>https://github.com/apache/struts/</url>
</scm>
</project>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
@@ -60,6 +60,9 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
}
public String start() throws BundleException {
clearErrorsAndMessages();
addActionMessage("Start - OSGi Host: " + osgiHost + ", ID: " + id);
Bundle bundle = osgiHost.getBundles().get(id);
try {
bundle.start();
@@ -69,29 +72,35 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
//there no easy way/elegant way to know if the bundle was processed already
Thread.sleep(1000);
} catch (Exception e) {
addActionError(e.toString());
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
}
return view();
}
public String stop() throws BundleException {
clearErrorsAndMessages();
addActionMessage("Stop - OSGi Host: " + osgiHost + ", ID: " + id);
Bundle bundle = osgiHost.getBundles().get(id);
try {
bundle.stop();
} catch (Exception e) {
addActionError(e.toString());
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
}
return view();
}
public String update() throws BundleException {
clearErrorsAndMessages();
addActionMessage("Update - OSGi Host: " + osgiHost + ", ID: " + id);
Bundle bundle = osgiHost.getBundles().get(id);
try {
bundle.update();
} catch (Exception e) {
addActionError(e.toString());
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
}
return view();
@@ -114,13 +123,14 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
}
public List<PackageConfig> getPackages() {
List<PackageConfig> pkgs = new ArrayList<PackageConfig>();
List<PackageConfig> pkgs = new ArrayList<>();
Bundle bundle = getBundle();
if (bundle.getState() == Bundle.ACTIVE) {
for (String name : bundleAccessor.getPackagesByBundle(bundle)) {
PackageConfig packageConfig = configuration.getPackageConfig(name);
if (packageConfig != null)
if (packageConfig != null) {
pkgs.add(packageConfig);
}
}
}
return pkgs;
@@ -133,13 +143,14 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
public Collection<Bundle> getBundles() {
List<Bundle> bundles = new ArrayList(osgiHost.getBundles().values());
Collections.sort(bundles, new Comparator<Bundle>() {
@Override
public int compare(Bundle bundle1, Bundle bundle2) {
boolean bundle1StrutsEnabled = isStrutsEnabled(bundle1);
boolean bundle2StrutsEnabled = isStrutsEnabled(bundle2);
if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled))
if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled)) {
return bundle1.getSymbolicName().compareTo(bundle2.getSymbolicName());
else {
} else {
return bundle1StrutsEnabled ? -1 : 1;
}
}
@@ -172,7 +183,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
try {
state = bundle.getState();
} catch (Exception e) {
addActionError("Unable to determine bundle state: " + e.getMessage());
addActionError("Unable to determine bundle state. Exception: " + e.toString() + " (" + e.getMessage() + ")");
return false;
}
@@ -181,8 +192,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
} else if ("stop".equals(val)) {
return state == Bundle.ACTIVE;
} else if ("update".equals(val)) {
return state == Bundle.ACTIVE || state == Bundle.INSTALLED
|| state == Bundle.RESOLVED;
return state == Bundle.ACTIVE || state == Bundle.INSTALLED || state == Bundle.RESOLVED;
}
throw new IllegalArgumentException("Invalid state");
}
@@ -197,6 +207,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
this.bundleAccessor = bundleAccessor;
}
@Override
public void setServletContext(ServletContext servletContext) {
osgiHost = (OsgiHost) servletContext.getAttribute(StrutsOsgiListener.OSGI_HOST);
}
@@ -24,20 +24,29 @@ package org.apache.struts2.osgi.admin.actions;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.apache.struts2.osgi.DefaultBundleAccessor;
import org.apache.felix.shell.ShellService;
import org.apache.struts2.osgi.DefaultBundleAccessor;
import org.apache.struts2.osgi.interceptor.BundleContextAware;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
/**
* This action executes commands on the Felix Shell
* This action executes commands on the Felix Shell.
*
* The action is BundleContextAware so that if the OSGi interceptor is used the BundleContext
* can be provided for configurations where the DefaultBundleAccessor is insufficient.
*
*/
public class ShellAction extends ActionSupport {
public class ShellAction extends ActionSupport implements BundleContextAware {
private String command;
private String output;
private BundleContext bundleContext;
@Override
public String execute() {
// get service
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
@@ -52,7 +61,9 @@ public class ShellAction extends ActionSupport {
outString = outByteStream.toString().trim();
errString = errByteStream.toString().trim();
} catch (Exception e) {
errString = e.getMessage();
outString = outByteStream.toString().trim();
errString = "Exception: " + e.toString() + " (" + e.getMessage() + "). Output:" + outString +
". Error: " + errByteStream.toString().trim(); // Full details for troubleshooting.
} finally {
outStream.close();
errStream.close();
@@ -75,17 +86,58 @@ public class ShellAction extends ActionSupport {
}
public void executeCommand(String commandLine, PrintStream out, PrintStream err) throws Exception {
ShellService shellService = getShellService();
if (shellService != null)
ShellService shellService = getShellService(out);
if (shellService != null) {
out.println("Attempting to execute command: " + commandLine);
shellService.executeCommand(commandLine, out, err);
}
else
err.println("Apache Felix Shell service is not installed");
}
private ShellService getShellService() {
private ShellService getShellService(PrintStream out) {
//bundle can be de-activated, so keeping a reference aorund is not a good idea
DefaultBundleAccessor bundleAcessor = DefaultBundleAccessor.getInstance();
ServiceReference ref = bundleAcessor.getServiceReference(ShellService.class.getName());
return (ShellService) bundleAcessor.getService(ref);
final DefaultBundleAccessor bundleAccessor = DefaultBundleAccessor.getInstance();
ServiceReference ref = (bundleAccessor != null ? bundleAccessor.getServiceReference(ShellService.class.getName()) : null);
//out.println("DefaultBundleAccessor: " + bundleAcessor + ", ServiceReference [" + ShellService.class.getName() + "]: " + ref); // No logger, for debugging only.
if (ref == null && this.bundleContext != null) {
// Depending on OSGi and Felix bundle configurations, the DefaultBundleAccessor may not be able to locate the ShellService.
// In such cases, use the bundleContext (if available) to locate the ShellService in a "brute-force" manner.
final Bundle[] bundles = this.bundleContext.getBundles();
if (bundles != null && bundles.length > 0) {
for (Bundle currentBundle : bundles) {
if (currentBundle != null) {
//out.println("Bundle [" + index + "], SymbolicName: " + currentBundle.getSymbolicName() + ", Location: " + currentBundle.getLocation() + ", BundleID: " + currentBundle.getBundleId()); // No logger, for debugging only.
if (currentBundle.getSymbolicName().startsWith("org.apache.felix.shell")) {
BundleContext currentBundleContext = currentBundle.getBundleContext();
Object directShellServiceByClass = (currentBundleContext != null ? currentBundleContext.getServiceReference(org.apache.felix.shell.ShellService.class) : null);
Object directShellServiceByName = (currentBundleContext != null ? currentBundleContext.getServiceReference("org.apache.felix.shell.ShellService") : null);
//out.println(" ShellService reference (via bundle's context) by class: " + directShellServiceByClass); // No logger, for debugging only.
//out.println(" ShellService reference (via bundle's context) by name: " + directShellServiceByName); // No logger, for debugging only.
if (ref == null) {
ref = (directShellServiceByClass != null ? (ServiceReference) directShellServiceByClass : (ServiceReference) directShellServiceByName);
}
}
} else {
//out.println("Bundle [" + index + "] is null"); // No logger, for debugging only.
}
}
} else {
//out.println("OSGi Interceptor-provided BundleContext bundle array is null or empty"); // No logger, for debugging only.
}
}
if (ref == null) {
out.println("ShellService reference cannot be found (null), service lookup will fail.");
}
return (ShellService) (bundleAccessor != null ? bundleAccessor.getService(ref) : null);
}
@Override
public void setBundleContext(BundleContext bundleContext) {
//System.out.println("ShellAction - setBundleContext called. BundleContext: " + bundleContext); // No logger, for debugging only.
this.bundleContext = bundleContext;
}
}
@@ -1,9 +1,13 @@
Copyright (c) 2009 Paul Bakaus, http://jqueryui.com/
Copyright jQuery Foundation and other contributors, https://jquery.org/
This software consists of voluntary contributions made by many
individuals (AUTHORS.txt, http://jqueryui.com/about) For exact
contribution history, see the revision history and logs, available
at http://jquery-ui.googlecode.com/svn/
individuals. For exact contribution history, see the revision history
available at https://github.com/jquery/jquery-ui
The following license applies to all parts of this software except as
documented below:
====
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -23,3 +27,17 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====
Copyright and related rights for sample code are waived via CC0. Sample
code is defined as all source code contained within the demos directory.
CC0: http://creativecommons.org/publicdomain/zero/1.0/
====
All files located in the node_modules and external directories are
externally maintained libraries used by this software which have their
own licenses; we recommend you read them, as their terms may differ from
the terms above.
+3 -2
View File
@@ -2,7 +2,8 @@ Apache Struts
Copyright 2000-2011 The Apache Software Foundation
This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).
The Apache Software Foundation (https://www.apache.org/).
The binary distributions includes the following third party software:
JQuery (http://jquery.com/).
jQuery (https://jquery.com/).
jQuery UI (https://jqueryui.com/).
@@ -24,11 +24,11 @@
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/shell.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
<script src="<@s.url value="/static/js/shell.js" />"></script>
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
</head>
<body>
<div class="menu">
@@ -23,10 +23,10 @@
<title>${bundle.symbolicName!}</title>
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
<script type="text/javascript">
$(function() {
@@ -163,5 +163,8 @@
</table>
</div>
</div>
<@s.actionmessage />
</body>
</html>
@@ -23,10 +23,10 @@
<title>OSGi Bundles</title>
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
</head>
<body>
@@ -44,6 +44,9 @@
</a>
</div>
</div>
<@s.actionerror />
<table class="properties" style="clear:both; width:700px">
<thead>
<tr>
@@ -87,5 +90,8 @@
</#list>
</tbody>
</table>
<@s.actionmessage />
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -1,404 +0,0 @@
/*
* jQuery UI CSS Framework
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.ui-helper-clearfix { display: inline-block; }
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix { height:1%; }
.ui-helper-clearfix { display:block; }
/* end clearfix */
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*
* jQuery UI CSS Framework
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande,%20Lucida%20Sans,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=06_inset_hard.png&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDefault=02_glass.png&bgImgOpacityDefault=85&borderColorDefault=c5dbec&fcDefault=2e6e9e&iconColorDefault=6da8d5&bgColorHover=d0e5f5&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=79b7e7&fcHover=1d5987&iconColorHover=217bc0&bgColorActive=f5f8f9&bgTextureActive=06_inset_hard.png&bgImgOpacityActive=100&borderColorActive=79b7e7&fcActive=e17009&iconColorActive=f9bd01&bgColorHighlight=fbec88&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fad42e&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1.1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #a6c9e2; background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x; color: #222222; }
.ui-widget-content a { color: #222222; }
.ui-widget-header { border: 1px solid #4297d7; background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #c5dbec; background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #2e6e9e; outline: none; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #2e6e9e; text-decoration: none; outline: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #79b7e7; background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1d5987; outline: none; }
.ui-state-hover a, .ui-state-hover a:hover { color: #1d5987; text-decoration: none; outline: none; }
.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #79b7e7; background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #e17009; outline: none; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #e17009; outline: none; text-decoration: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fad42e; background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
.ui-state-error a, .ui-widget-content .ui-state-error a { color: #cd0a0a; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_469bdd_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_469bdd_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_d8e7f3_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_6da8d5_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_217bc0_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_f9bd01_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; }
.ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
.ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
.ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
.ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
.ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
.ui-corner-right { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
.ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
.ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; }/* Accordion
----------------------------------*/
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
.ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
----------------------------------*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}/* Dialog
----------------------------------*/
.ui-dialog { position: relative; padding: .2em; width: 300px; }
.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/* Progressbar
----------------------------------*/
.ui-progressbar { height:2em; text-align: left; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
----------------------------------*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
----------------------------------*/
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
----------------------------------*/
.ui-tabs { padding: .2em; zoom: 1; }
.ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
.ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -29,8 +29,8 @@
result_div.appendChild(document.createElement('br'));
for (var line_index in result_array) {
var result_wrap = document.createElement('pre')
line = document.createTextNode(result_array[line_index]);
var result_wrap = document.createElement('pre');
var line = document.createTextNode(result_array[line_index]);
result_wrap.appendChild(line);
result_div.appendChild(result_wrap);
result_div.appendChild(document.createElement('br'));
+6 -5
View File
@@ -21,28 +21,29 @@
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="bundle-admin" namespace="/osgi/admin" extends="struts-default">
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<package name="bundle-admin" namespace="/osgi/admin" extends="osgi-default" strict-method-invocation="true">
<default-action-ref name="bundles" />
<action name="bundle_*!*" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="{2}">
<param name="id">{1}</param>
<result type="freemarker">viewBundle.ftl</result>
<allowed-methods>index,view,start,stop,update</allowed-methods>
</action>
<action name="bundles" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="index">
<result type="freemarker">viewBundles.ftl</result>
</action>
<action name="execCommand" class="org.apache.struts2.osgi.admin.actions.ShellAction">
<result type="freemarker">commandResult.ftl</result>
</action>
<action name="shell">
<action name="shell" class="org.apache.struts2.osgi.admin.actions.ShellAction">
<result type="freemarker">shell.ftl</result>
</action>
</package>
+1 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
@@ -67,7 +67,6 @@
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>2.1.0</version>
<configuration>
<instructions>
<manifestLocation>META-INF</manifestLocation>
@@ -22,13 +22,21 @@ package actions.osgi;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
@ResultPath("/content")
@Namespace("/osgi")
@ResultPath("/content/osgi")
public class HelloWorldAction extends ActionSupport {
private Message message;
private Message message = new Message("Default non-null message");
@Action("hello-convention")
@Override
@Actions({
@Action(value="hello-convention", results={@Result(name="success", type="freemarker", location="/content/osgi/hello-convention.ftl")}),
@Action(value="/osgi/hello-convention", results={@Result(name="success", type="freemarker", location="/content/osgi/hello-convention.ftl")})
})
public String execute() {
return SUCCESS;
}
@@ -45,6 +53,7 @@ public class HelloWorldAction extends ActionSupport {
return "Hello!!!";
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{message:");
sb.append(message != null ? message.getText() : "null");
@@ -20,7 +20,7 @@
-->
<html>
<head>
<title>Action mapped by the XML configurationn</title>
<title>Action mapped by the XML configuration</title>
</head>
<body>
This is an action mapped by XML configuration, using a <b>FreeMarker</b> result.
@@ -18,7 +18,7 @@
*#
<html>
<head>
<title>Action mapped by the XML configurationn</title>
<title>Action mapped by the XML configuration</title>
</head>
<body>
This is an action mapped by XML configuration, using a <b>Velocity</b> result.
+21 -6
View File
@@ -20,21 +20,36 @@
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="bundle-demo" namespace="/osgi" extends="osgi-default">
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
Note: The constant definitions specified here (within the demo bundle) may also need to be
duplicated in the struts.xml configuration of the outer project that packages the demo
bundle JAR file within it.
-->
<constant name="struts.objectFactory" value="osgi" />
<constant name="struts.objectFactory.delegate" value="struts" />
<constant name="struts.freemarker.manager.classname" value="org.apache.struts2.osgi.BundleFreemarkerManager" />
<constant name="struts.velocity.manager.classname" value="org.apache.struts2.osgi.VelocityBundleResourceLoader" />
<constant name="struts.staticContentLoader" value="org.apache.struts2.osgi.loaders.StaticContentBundleResourceLoader" />
<package name="bundle-demo" namespace="/osgi" extends="osgi-default" strict-method-invocation="true">
<default-action-ref name="home" />
<action name="hello-velocity" class="helloWorldAction">
<action name="hello-velocity" class="actions.osgi.HelloWorldAction">
<result type="velocity">/content/osgi/hello.vm</result>
</action>
<action name="hello-freemarker" class="helloWorldAction">
<action name="hello-freemarker" class="actions.osgi.HelloWorldAction">
<result type="freemarker">/content/osgi/hello.ftl</result>
</action>
<action name="home">
<action name="home" class="actions.osgi.HelloWorldAction">
<result type="freemarker">/content/osgi/home.ftl</result>
</action>
</package>
</struts>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+3 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -190,7 +190,8 @@
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.5.0</version>
<version>1.7.0</version>
<!-- Match version of minimum compatible version -->
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
@@ -36,10 +36,8 @@ import java.util.*;
* @since 2.1
*/
public abstract class AbstractMatcher<E> implements Serializable {
/**
* <p> The logging instance </p>
*/
private static final Logger log = LogManager.getLogger(AbstractMatcher.class);
private static final Logger LOG = LogManager.getLogger(AbstractMatcher.class);
/**
* <p> Handles all wildcard pattern matching. </p>
@@ -50,10 +48,34 @@ public abstract class AbstractMatcher<E> implements Serializable {
* <p> The compiled patterns and their associated target objects </p>
*/
List<Mapping<E>> compiledPatterns = new ArrayList<>();
;
/**
* This flag controls if passed named params should be appended
* to the map in {@link #replaceParameters(Map, Map)}
* and will be accessible in {@link com.opensymphony.xwork2.config.entities.ResultConfig}.
* If set to false, the named parameters won't be appended.
*
* This behaviour is controlled by {@link org.apache.struts2.StrutsConstants#STRUTS_MATCHER_APPEND_NAMED_PARAMETERS}
*
* @since 2.5.23
* See WW-5065
*/
private final boolean appendNamedParameters;
public AbstractMatcher(PatternMatcher<?> helper) {
public AbstractMatcher(PatternMatcher<?> helper, boolean appendNamedParameters) {
this.wildcard = (PatternMatcher<Object>) helper;
this.appendNamedParameters = appendNamedParameters;
}
/**
* Creates a matcher with {@link #appendNamedParameters} set to true to keep backward compatibility
*
* @param helper an instance of {@link PatternMatcher}
* @deprecated use @{link {@link AbstractMatcher(PatternMatcher, boolean)} instead
*/
@Deprecated
public AbstractMatcher(PatternMatcher<?> helper) {
this(helper, true);
}
/**
@@ -84,17 +106,17 @@ public abstract class AbstractMatcher<E> implements Serializable {
name = name.substring(1);
}
log.debug("Compiling pattern '{}'", name);
LOG.debug("Compiling pattern '{}'", name);
pattern = wildcard.compilePattern(name);
compiledPatterns.add(new Mapping<E>(name, pattern, target));
compiledPatterns.add(new Mapping<>(name, pattern, target));
if (looseMatch) {
int lastStar = name.lastIndexOf('*');
if (lastStar > 1 && lastStar == name.length() - 1) {
if (name.charAt(lastStar - 1) != '*') {
pattern = wildcard.compilePattern(name.substring(0, lastStar - 1));
compiledPatterns.add(new Mapping<E>(name, pattern, target));
compiledPatterns.add(new Mapping<>(name, pattern, target));
}
}
}
@@ -115,12 +137,12 @@ public abstract class AbstractMatcher<E> implements Serializable {
E config = null;
if (compiledPatterns.size() > 0) {
log.debug("Attempting to match '{}' to a wildcard pattern, {} available", potentialMatch, compiledPatterns.size());
LOG.debug("Attempting to match '{}' to a wildcard pattern, {} available", potentialMatch, compiledPatterns.size());
Map<String,String> vars = new LinkedHashMap<String,String>();
Map<String,String> vars = new LinkedHashMap<>();
for (Mapping<E> m : compiledPatterns) {
if (wildcard.match(vars, potentialMatch, m.getPattern())) {
log.debug("Value matches pattern '{}'", m.getOriginalPattern());
LOG.debug("Value matches pattern '{}'", m.getOriginalPattern());
config = convert(potentialMatch, m.getTarget(), vars);
break;
}
@@ -152,20 +174,23 @@ public abstract class AbstractMatcher<E> implements Serializable {
*/
protected Map<String,String> replaceParameters(Map<String, String> orig, Map<String,String> vars) {
Map<String, String> map = new LinkedHashMap<>();
//this will set the group index references, like {1}
for (Map.Entry<String,String> entry : orig.entrySet()) {
map.put(entry.getKey(), convertParam(entry.getValue(), vars));
}
//the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
//now add the non-numeric values
for (Map.Entry<String,String> entry: vars.entrySet()) {
if (!NumberUtils.isCreatable(entry.getKey())) {
map.put(entry.getKey(), entry.getValue());
if (appendNamedParameters) {
LOG.debug("Appending named parameters to the result map");
//the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
//now add the non-numeric values
for (Map.Entry<String,String> entry: vars.entrySet()) {
if (!NumberUtils.isCreatable(entry.getKey())) {
map.put(entry.getKey(), entry.getValue());
}
}
}
return map;
}
@@ -192,7 +217,7 @@ public abstract class AbstractMatcher<E> implements Serializable {
c = val.charAt(x);
if (x < len - 2 &&
c == '{' && '}' == val.charAt(x+2)) {
varVal = (String)vars.get(String.valueOf(val.charAt(x + 1)));
varVal = vars.get(String.valueOf(val.charAt(x + 1)));
if (varVal != null) {
ret.append(varVal);
}
@@ -213,18 +238,18 @@ public abstract class AbstractMatcher<E> implements Serializable {
/**
* <p> The original pattern. </p>
*/
private String original;
private final String original;
/**
* <p> The compiled pattern. </p>
*/
private Object pattern;
private final Object pattern;
/**
* <p> The original object. </p>
*/
private E config;
private final E config;
/**
* <p> Contructs a read-only Mapping instance. </p>
@@ -58,7 +58,33 @@ public class ActionConfigMatcher extends AbstractMatcher<ActionConfig> implement
public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
Map<String, ActionConfig> configs,
boolean looseMatch) {
super(patternMatcher);
this(patternMatcher, configs, looseMatch, true);
}
/**
* <p> Finds and precompiles the wildcard patterns from the ActionConfig
* "path" attributes. ActionConfig's will be evaluated in the order they
* exist in the config file. Only paths that actually contain a
* wildcard will be compiled. </p>
*
* <p>Patterns can optionally be matched "loosely". When
* the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
* wildcard), if the pattern fails, it is also matched as if the
* last two characters didn't exist. The goal is to support the
* legacy "*!*" syntax, where the "!*" is optional.</p>
*
* @param patternMatcher pattern matcher
* @param configs An array of ActionConfig's to process
* @param looseMatch To loosely match wildcards or not
* @param appendNamedParameters To append named parameters or not
*
* @since 2.5.23
* See WW-5065
*/
public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
Map<String, ActionConfig> configs,
boolean looseMatch, boolean appendNamedParameters) {
super(patternMatcher, appendNamedParameters);
for (Map.Entry<String, ActionConfig> entry : configs.entrySet()) {
addPattern(entry.getKey(), entry.getValue(), looseMatch);
}
@@ -289,6 +289,8 @@ public class DefaultConfiguration implements Configuration {
builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
builder.constant(StrutsConstants.STRUTS_I18N_RELOAD, "false");
builder.constant(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, "true");
return builder.create(true);
}
@@ -338,8 +340,12 @@ public class DefaultConfiguration implements Configuration {
}
PatternMatcher<int[]> matcher = container.getInstance(PatternMatcher.class);
boolean appendNamedParameters = Boolean.parseBoolean(
container.getInstance(String.class, StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS)
);
return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs),
Collections.unmodifiableMap(namespaceConfigs), matcher);
Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters);
}
private void setDefaultResults(Map<String, ResultConfig> results, PackageConfig packageContext) {
@@ -417,15 +423,18 @@ public class DefaultConfiguration implements Configuration {
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
Map<String, String> namespaceConfigs,
PatternMatcher<int[]> matcher) {
PatternMatcher<int[]> matcher,
boolean appendNamedParameters)
{
this.namespaceActionConfigs = namespaceActionConfigs;
this.namespaceConfigs = namespaceConfigs;
this.namespaceActionConfigMatchers = new LinkedHashMap<>();
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet());
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet(), appendNamedParameters);
for (Map.Entry<String, Map<String, ActionConfig>> entry : namespaceActionConfigs.entrySet()) {
namespaceActionConfigMatchers.put(entry.getKey(), new ActionConfigMatcher(matcher, entry.getValue(), true));
ActionConfigMatcher configMatcher = new ActionConfigMatcher(matcher, entry.getValue(), true, appendNamedParameters);
namespaceActionConfigMatchers.put(entry.getKey(), configMatcher);
}
}
@@ -29,9 +29,23 @@ import java.util.Set;
* @since 2.1
*/
public class NamespaceMatcher extends AbstractMatcher<NamespaceMatch> {
public NamespaceMatcher(PatternMatcher<?> patternMatcher,
Set<String> namespaces) {
super(patternMatcher);
public NamespaceMatcher(PatternMatcher<?> patternMatcher, Set<String> namespaces) {
this(patternMatcher, namespaces, true);
}
/**
* Matches namespace strings against a wildcard pattern matcher
*
* @param patternMatcher pattern matcher
* @param namespaces A set of namespaces to process
* @param appendNamedParameters To append named parameters or not
*
* @since 2.5.23
* See WW-5065
*/
public NamespaceMatcher(PatternMatcher<?> patternMatcher, Set<String> namespaces, boolean appendNamedParameters) {
super(patternMatcher, appendNamedParameters);
for (String name : namespaces) {
if (!patternMatcher.isLiteral(name)) {
addPattern(name, new NamespaceMatch(name, null), false);
@@ -108,6 +108,7 @@ import com.opensymphony.xwork2.validator.DefaultValidatorFactory;
import com.opensymphony.xwork2.validator.DefaultValidatorFileParser;
import com.opensymphony.xwork2.validator.ValidatorFactory;
import com.opensymphony.xwork2.validator.ValidatorFileParser;
import com.sun.org.apache.xpath.internal.operations.Bool;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
@@ -227,6 +228,7 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
props.setProperty(XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, Boolean.FALSE.toString());
props.setProperty(XWorkConstants.RELOAD_XML_CONFIGURATION, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE.toString());
}
}
@@ -32,6 +32,7 @@ import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import java.util.Map;
@@ -173,7 +174,10 @@ public class AliasInterceptor extends AbstractInterceptor {
HttpParameters contextParameters = ActionContext.getContext().getParameters();
if (null != contextParameters) {
value = new Evaluated(contextParameters.get(name));
Parameter param = contextParameters.get(name);
if (param.isDefined()) {
value = new Evaluated(param.getValue());
}
}
}
if (value.isDefined()) {
@@ -25,13 +25,16 @@ import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.util.*;
import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.MemberAccessValueStack;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import java.util.Collection;
import java.util.Comparator;
@@ -100,8 +103,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
*/
static final Comparator<String> rbCollator = new Comparator<String>() {
public int compare(String s1, String s2) {
int l1 = countOGNLCharacters(s1),
l2 = countOGNLCharacters(s2);
int l1 = countOGNLCharacters(s1);
int l2 = countOGNLCharacters(s2);
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
}
@@ -185,7 +188,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
if (clearableStack) {
//if the stack's context can be cleared, do that to prevent OGNL
//from having access to objects in the stack, see XW-641
((ClearableValueStack)newStack).clearContextValues();
((ClearableValueStack) newStack).clearContextValues();
Map<String, Object> context = newStack.getContext();
ReflectionContextState.setCreatingNullObjects(context, true);
ReflectionContextState.setDenyMethodExecution(context, true);
@@ -228,7 +231,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
TextProvider tp = (TextProvider) action;
developerNotification = tp.getText("devmode.notification",
"Developer Notification:\n{0}",
new String[]{ developerNotification }
new String[]{developerNotification}
);
}
@@ -245,7 +248,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
/**
* Checks if name of parameter can be accepted or thrown away
*
* @param name parameter name
* @param name parameter name
* @param action current action
* @return true if parameter is accepted
*/
@@ -289,27 +292,45 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return accepted;
}
protected boolean isWithinLengthLimit( String name ) {
protected boolean isWithinLengthLimit(String name) {
boolean matchLength = name.length() <= paramNameMaxLength;
if (!matchLength) {
LOG.debug("Parameter [{}] is too long, allowed length is [{}]", name, String.valueOf(paramNameMaxLength));
if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
"to override the limit, see more at\n" +
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
name, paramNameMaxLength);
} else {
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
}
}
return matchLength;
}
}
protected boolean isAccepted(String paramName) {
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
if (result.isAccepted()) {
return true;
} else if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getAcceptedPattern());
} else {
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
}
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
return false;
}
protected boolean isExcluded(String paramName) {
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
if (result.isExcluded()) {
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
if (devMode) { // warn only when in devMode
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/#accepted--excluded-patterns",
paramName, result.getExcludedPattern());
} else {
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
}
return true;
}
return false;
@@ -21,8 +21,10 @@ package com.opensymphony.xwork2.security;
import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.util.Arrays;
import java.util.Collections;
@@ -35,7 +37,11 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
private static final Logger LOG = LogManager.getLogger(DefaultAcceptedPatternsChecker.class);
public static final String[] ACCEPTED_PATTERNS = {
"\\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'\\])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*"
"\\w+((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*"
};
public static final String[] DMI_AWARE_ACCEPTED_PATTERNS = {
"\\w+([:]?\\w+)?((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*([!]?\\w+)?"
};
private Set<Pattern> acceptedPatterns;
@@ -44,10 +50,21 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
setAcceptedPatterns(ACCEPTED_PATTERNS);
}
public DefaultAcceptedPatternsChecker(
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false) String dmiValue
) {
if (BooleanUtils.toBoolean(dmiValue)) {
LOG.debug("DMI is enabled, adding DMI related accepted patterns");
setAcceptedPatterns(DMI_AWARE_ACCEPTED_PATTERNS);
} else {
setAcceptedPatterns(ACCEPTED_PATTERNS);
}
}
@Inject(value = XWorkConstants.OVERRIDE_ACCEPTED_PATTERNS, required = false)
protected void setOverrideAcceptedPatterns(String acceptablePatterns) {
LOG.warn("Overriding accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
acceptedPatterns, acceptablePatterns);
acceptedPatterns, acceptablePatterns);
acceptedPatterns = new HashSet<>();
try {
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
@@ -88,7 +105,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
LOG.debug("Sets accepted patterns to [{}], note this impacts the safety of your application!", patterns);
} else {
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
acceptedPatterns, patterns);
acceptedPatterns, patterns);
}
acceptedPatterns = new HashSet<>(patterns.size());
try {
@@ -81,7 +81,7 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
@Inject(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION)
protected void setDynamicMethodInvocation(String dmiValue) {
if (BooleanUtils.toBoolean(dmiValue) == false) {
if (!BooleanUtils.toBoolean(dmiValue)) {
LOG.debug("DMI is disabled, adding DMI related excluded patterns");
setAdditionalExcludePatterns("^(action|method):.*");
}
@@ -58,10 +58,25 @@ public class ResourceFinder {
this(path, classLoaderInterface, null);
}
/**
* Create a ResourceFinder instance for looking up resources (via ClassLoader or via specific URLs
* specifying resource locations).
*
* This class was functional in Struts 2.3.x, but broken for Struts 2.5.x (before 2.5.24), when dealing with
* JAR resources in certain circumstances. The current logic permits the base path to be "" (empty string),
* which is required to also match JAR entries rooted at "" and not just file entries rooted at "/".
*
* @param path Base path from which to look for resources (typically "xyz/abc/klm" form for file or
* jar contents).
* @param classLoaderInterface ClassLoader to perform the resource lookup. If null, a default Thread
* ClassLoader will be used.
* @param urls URLs (typically file: or jar:) within which to search for resources, instead of the
* ClassLoader. If null, fallback to a ClassLoader instead.
*/
public ResourceFinder(String path, ClassLoaderInterface classLoaderInterface, URL... urls) {
path = StringUtils.trimToEmpty(path);
if (!StringUtils.endsWith(path, "/")) {
path += "/";
if (!path.isEmpty() && !StringUtils.endsWith(path, "/")) {
path += "/"; // Only append terminator to nonempty paths, otherwise JAR entry lookups break.
}
this.path = path;
@@ -1015,15 +1030,26 @@ public class ResourceFinder {
public Map<URL, Set<String>> findPackagesMap(String uri) throws IOException {
String basePath = path + uri;
LOG.trace(" basePath(initial): " + basePath);
if (!basePath.endsWith("/")) {
basePath += "/";
}
LOG.trace(" basePath(final): " + basePath);
Enumeration<URL> urls = getResources(basePath);
Map<URL, Set<String>> result = new HashMap<>();
if (! urls.hasMoreElements()) {
LOG.debug(" urls enumeration for basePath is empty ?");
}
while (urls.hasMoreElements()) {
URL location = urls.nextElement();
LOG.debug(" url (location): " + location);
try {
if ("jar".equals(location.getProtocol())) {
Set<String> resources = new HashSet<>();
@@ -1114,12 +1140,20 @@ public class ResourceFinder {
jarfile = conn.getJarFile();
Enumeration<JarEntry> entries = jarfile.entries();
if (entries == null || ! entries.hasMoreElements()) {
LOG.debug(" JAR entries null or empty");
}
LOG.debug(" Looking for entries matching basePath: " + basePath);
while (entries != null && entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (entry.isDirectory() && StringUtils.startsWith(name, basePath)) {
resources.add(name);
} else if (entry.isDirectory()) {
LOG.trace(" entry: " + name + " , isDirectory: " + entry.isDirectory() + " but does not start with basepath");
}
}
}
@@ -1156,13 +1190,22 @@ public class ResourceFinder {
private Enumeration<URL> getResources(String fulluri) throws IOException {
if (urls == null) {
LOG.debug(" urls (member) null, using classLoaderInterface to get resources");
return classLoaderInterface.getResources(fulluri);
}
LOG.debug(" urls (member) non-null, using findResource to get resources");
Vector<URL> resources = new Vector<>();
for (URL url : urls) {
URL resource = findResource(fulluri, url);
if (resource != null){
if (resource != null) {
LOG.trace(" resource lookup non-null");
resources.add(resource);
} else {
LOG.trace(" resource lookup is null");
}
}
return resources.elements();
@@ -341,4 +341,7 @@ public final class StrutsConstants {
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
public static final String STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT = "struts.ognl.autoGrowthCollectionLimit";
/** See {@link com.opensymphony.xwork2.config.impl.AbstractMatcher#appendNamedParameters */
public static final String STRUTS_MATCHER_APPEND_NAMED_PARAMETERS = "struts.matcher.appendNamedParameters";
}
@@ -31,8 +31,7 @@ import com.opensymphony.xwork2.util.ValueStack;
* complex expression and then simply reference that variable each time rather than the complex expression. This is
* useful in both cases: when the complex expression takes time (performance improvement) or is hard to read (code
* readability improvement).</p>
* <p>If the tag is used with body content, the evaluation of the value parameter is omitted. Instead, the String to
* which the body evaluates is set as value for the scoped variable.</p>
* <p>If the value parameter is omitted, the String to which the body evaluates is set as value for the scoped variable.</p>
*
* <p>The scopes available are as follows:</p>
* <ul>
@@ -94,10 +93,10 @@ public class Set extends ContextBean {
Object o;
if (value == null) {
if (body != null && !body.equals("")) {
o = body;
} else {
if (body == null) {
o = findValue("top");
} else {
o = body;
}
} else {
o = findValue(value);
@@ -696,12 +696,23 @@ public class Dispatcher {
try {
locale = LocaleUtils.toLocale(defaultLocale);
} catch (IllegalArgumentException e) {
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
defaultLocale, request.getLocale()), e);
locale = request.getLocale();
try {
locale = request.getLocale();
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
defaultLocale, locale), e);
} catch (RuntimeException rex) {
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, and cannot get locale from HTTP Request, falling back to system default locale",
defaultLocale), rex);
locale = Locale.getDefault();
}
}
} else {
locale = request.getLocale();
try {
locale = request.getLocale();
} catch (RuntimeException rex) {
LOG.warn("Cannot get locale from HTTP Request, falling back to system default locale", rex);
locale = Locale.getDefault();
}
}
return locale;
}
@@ -343,6 +343,14 @@ public class FileUploadInterceptor extends AbstractInterceptor {
validation.addFieldError(inputName, errMsg);
}
if (LOG.isWarnEnabled()) {
LOG.warn(errMsg);
}
} else if (file.getContent() == null) {
String errMsg = getTextMessage(action, "struts.messages.error.uploading", new String[]{filename});
if (validation != null) {
validation.addFieldError(inputName, errMsg);
}
if (LOG.isWarnEnabled()) {
LOG.warn(errMsg);
}
@@ -68,9 +68,13 @@ public class SetTag extends ContextBeanTag {
@Override
protected String getBody() {
if (trimBody) {
return super.getBody();
if (bodyContent == null) {
return null;
} else {
return bodyContent.getString().trim();
}
} else {
return (bodyContent == null ? "" : bodyContent.getString());
return (bodyContent == null ? null : bodyContent.getString());
}
}
}
@@ -24,6 +24,7 @@ import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.util.WildcardHelper;
import org.apache.struts2.util.RegexPatternMatcher;
import java.util.HashMap;
import java.util.Map;
@@ -142,6 +143,79 @@ public class ActionConfigMatcherTest extends XWorkTestCase {
}
/**
* Test to make sure the {@link AbstractMatcher#replaceParameters(Map, Map)} method isn't adding values to the
* return value.
*/
public void testReplaceParametersWithNoAppendingParams() {
Map<String, ActionConfig> map = new HashMap<>();
HashMap<String, String> params = new HashMap<>();
params.put("first", "{1}");
ActionConfig config = new ActionConfig.Builder("package", "foo/{one}/{two}/{three}", "foo.bar.Action")
.addParams(params)
.addExceptionMapping(new ExceptionMappingConfig.Builder("foo{1}", "java.lang.{2}Exception", "success{1}")
.addParams(new HashMap<>(params))
.build())
.addResultConfig(new ResultConfig.Builder("success{1}", "foo.{2}").addParams(params).build())
.setStrictMethodInvocation(false)
.build();
map.put("foo/{one}/{two}/{three}", config);
ActionConfigMatcher replaceMatcher = new ActionConfigMatcher(new RegexPatternMatcher(), map, false, false);
ActionConfig matched = replaceMatcher.match("foo/paramOne/paramTwo/paramThree");
assertNotNull("ActionConfig should be matched", matched);
// Verify all The ActionConfig, ExceptionConfig, and ResultConfig have the correct number of params
assertEquals("The ActionConfig should have the correct number of params", 1, matched.getParams().size());
assertEquals("The ExceptionMappingConfigs should have the correct number of params", 1, matched.getExceptionMappings().get(0).getParams().size());
assertEquals("The ResultConfigs should have the correct number of params", 1, matched.getResults().get("successparamOne").getParams().size());
// Verify the params are still getting their values replaced correctly
assertEquals("The ActionConfig params have replaced values", "paramOne", matched.getParams().get("first"));
assertEquals("The ActionConfig params have replaced values", "paramOne", matched.getExceptionMappings().get(0).getParams().get("first"));
assertEquals("The ActionConfig params have replaced values", "paramOne", matched.getResults().get("successparamOne").getParams().get("first"));
}
/**
* Test to make sure the {@link AbstractMatcher#replaceParameters(Map, Map)} method is adding values to the
* return value.
*/
public void testReplaceParametersWithAppendingParams() {
Map<String, ActionConfig> map = new HashMap<>();
HashMap<String, String> params = new HashMap<>();
params.put("first", "{1}");
ActionConfig config = new ActionConfig.Builder("package", "foo/{one}/{two}/{three}", "foo.bar.Action")
.addParams(params)
.addExceptionMapping(new ExceptionMappingConfig.Builder("foo{1}", "java.lang.{2}Exception", "success{1}")
.addParams(new HashMap<>(params))
.build())
.addResultConfig(new ResultConfig.Builder("success{1}", "foo.{2}").addParams(params).build())
.setStrictMethodInvocation(false)
.build();
map.put("foo/{one}/{two}/{three}", config);
ActionConfigMatcher replaceMatcher = new ActionConfigMatcher(new RegexPatternMatcher(), map, false, true);
ActionConfig matched = replaceMatcher.match("foo/paramOne/paramTwo/paramThree");
assertNotNull("ActionConfig should be matched", matched);
assertEquals(4, matched.getParams().size());
assertEquals(4, matched.getExceptionMappings().get(0).getParams().size());
assertEquals(4, matched.getResults().get("successparamOne").getParams().size());
// Verify the params are still getting their values replaced correctly
assertEquals("paramOne", matched.getParams().get("first"));
assertEquals("paramOne", matched.getParams().get("one"));
assertEquals("paramTwo", matched.getParams().get("two"));
assertEquals("paramThree", matched.getParams().get("three"));
assertEquals("paramOne", matched.getExceptionMappings().get(0).getParams().get("first"));
assertEquals("paramOne", matched.getExceptionMappings().get(0).getParams().get("one"));
assertEquals("paramTwo", matched.getExceptionMappings().get(0).getParams().get("two"));
assertEquals("paramThree", matched.getExceptionMappings().get(0).getParams().get("three"));
assertEquals("paramOne", matched.getResults().get("successparamOne").getParams().get("first"));
}
private Map<String,ActionConfig> buildActionConfigMap() {
Map<String, ActionConfig> map = new HashMap<>();
@@ -23,6 +23,7 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.mock.MockActionProxy;
import org.apache.struts2.dispatcher.HttpParameters;
import java.util.HashMap;
import java.util.Map;
@@ -60,7 +61,30 @@ public class AliasInterceptorTest extends XWorkTestCase {
actionOne.setFoo(17);
actionOne.setBar(23);
proxy.execute();
assertEquals("name to be copied", actionOne.getAliasSource());
assertEquals(actionOne.getAliasSource(), actionOne.getAliasDest());
assertNull(actionOne.getBlah()); // WW-5087
}
public void testNotExisting() throws Exception {
Map<String, Object> params = new HashMap<>();
Map<String, Object> httpParams = new HashMap<>();
httpParams.put("notExisting", "from http parameter");
params.put(ActionContext.PARAMETERS, HttpParameters.create(httpParams).build());
XmlConfigurationProvider provider = new XmlConfigurationProvider("xwork-sample.xml");
container.inject(provider);
loadConfigurationProviders(provider);
ActionProxy proxy = actionProxyFactory.createActionProxy("", "aliasTest", null, params);
SimpleAction actionOne = (SimpleAction) proxy.getAction();
// prevent ERROR result
actionOne.setFoo(-1);
actionOne.setBar(1);
proxy.execute();
assertEquals("from http parameter", actionOne.getBlah());
assertNull(actionOne.getAliasDest()); // WW-5087
}
public void testInvalidAliasExpression() throws Exception {
@@ -27,41 +27,41 @@ import java.util.regex.Pattern;
public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
public void testHardcodedAcceptedPatterns() throws Exception {
// given
List<String> params = new ArrayList<String>() {
{
add("%{#application['test']}");
add("%{#application.test}");
add("%{#Application['test']}");
add("%{#Application.test}");
add("%{#session['test']}");
add("%{#session.test}");
add("%{#Session['test']}");
add("%{#Session.test}");
add("%{#struts['test']}");
add("%{#struts.test}");
add("%{#Struts['test']}");
add("%{#Struts.test}");
add("%{#request['test']}");
add("%{#request.test}");
add("%{#Request['test']}");
add("%{#Request.test}");
add("%{#servletRequest['test']}");
add("%{#servletRequest.test}");
add("%{#ServletRequest['test']}");
add("%{#ServletRequest.test}");
add("%{#servletResponse['test']}");
add("%{#servletResponse.test}");
add("%{#ServletResponse['test']}");
add("%{#ServletResponse.test}");
add("%{#parameters['test']}");
add("%{#parameters.test}");
add("%{#Parameters['test']}");
add("%{#Parameters.test}");
}
};
private final List<String> params = new ArrayList<String>() {
{
add("%{#application['test']}");
add("%{#application.test}");
add("%{#Application['test']}");
add("%{#Application.test}");
add("%{#session['test']}");
add("%{#session.test}");
add("%{#Session['test']}");
add("%{#Session.test}");
add("%{#struts['test']}");
add("%{#struts.test}");
add("%{#Struts['test']}");
add("%{#Struts.test}");
add("%{#request['test']}");
add("%{#request.test}");
add("%{#Request['test']}");
add("%{#Request.test}");
add("%{#servletRequest['test']}");
add("%{#servletRequest.test}");
add("%{#ServletRequest['test']}");
add("%{#ServletRequest.test}");
add("%{#servletResponse['test']}");
add("%{#servletResponse.test}");
add("%{#ServletResponse['test']}");
add("%{#ServletResponse.test}");
add("%{#parameters['test']}");
add("%{#parameters.test}");
add("%{#Parameters['test']}");
add("%{#Parameters.test}");
}
};
public void testHardcodedAcceptedPatterns() {
// given
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker();
for (String param : params) {
@@ -73,7 +73,20 @@ public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
}
}
public void testUnderscoreInParamName() throws Exception {
public void testHardcodedAcceptedPatternsWithDmiEnabled() {
// given
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker(Boolean.TRUE.toString());
for (String param : params) {
// when
AcceptedPatternsChecker.IsAccepted actual = checker.isAccepted(param);
// then
assertFalse("Access to " + param + " is possible!", actual.isAccepted());
}
}
public void testUnderscoreInParamName() {
// given
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker();
@@ -84,22 +97,33 @@ public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
assertTrue("Param with underscore wasn't accepted!", actual.isAccepted());
}
public void testAcceptedPatternsImmutable() throws Exception {
public void testUnderscoreInParamNameWithDmiEnabled() {
// given
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker(Boolean.TRUE.toString());
// when
AcceptedPatternsChecker.IsAccepted actual = checker.isAccepted("mapParam['param_1']");
// then
assertTrue("Param with underscore wasn't accepted!", actual.isAccepted());
}
public void testAcceptedPatternsImmutable() {
AcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker();
Set<Pattern> acceptedPatternSet = checker.getAcceptedPatterns();
assertNotNull("default accepted patterns null?", acceptedPatternSet);
assertFalse("default accepted patterns empty?", acceptedPatternSet.isEmpty());
try {
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern") );
fail ("accepted patterns modifiable?");
} catch(UnsupportedOperationException uoe) {
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern"));
fail("accepted patterns modifiable?");
} catch (UnsupportedOperationException uoe) {
// Expected result
}
try {
acceptedPatternSet.clear();
fail ("accepted patterns modifiable?");
} catch(UnsupportedOperationException uoe) {
fail("accepted patterns modifiable?");
} catch (UnsupportedOperationException uoe) {
// Expected result
}
@@ -108,15 +132,15 @@ public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
assertNotNull("replaced default accepted patterns null?", acceptedPatternSet);
assertFalse("replaced default accepted patterns empty?", acceptedPatternSet.isEmpty());
try {
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern") );
fail ("replaced accepted patterns modifiable?");
} catch(UnsupportedOperationException uoe) {
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern"));
fail("replaced accepted patterns modifiable?");
} catch (UnsupportedOperationException uoe) {
// Expected result
}
try {
acceptedPatternSet.clear();
fail ("accepted patterns modifiable?");
} catch(UnsupportedOperationException uoe) {
fail("accepted patterns modifiable?");
} catch (UnsupportedOperationException uoe) {
// Expected result
}
@@ -125,23 +149,29 @@ public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
acceptedPatternSet = checker.getAcceptedPatterns();
assertNotNull("replaced default accepted patterns null?", acceptedPatternSet);
assertFalse("replaced default accepted patterns empty?", acceptedPatternSet.isEmpty());
assertTrue("replaced default accepted patterns not size " + testPatternArray.length + "?",
acceptedPatternSet.size() == testPatternArray.length);
assertEquals("replaced default accepted patterns not size " + testPatternArray.length + "?", acceptedPatternSet.size(), testPatternArray.length);
for (String testPatternArray1 : testPatternArray) {
assertTrue(testPatternArray1 + " not accepted?", checker.isAccepted(testPatternArray1).isAccepted());
}
try {
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern") );
fail ("replaced accepted patterns modifiable?");
} catch(UnsupportedOperationException uoe) {
acceptedPatternSet.add(Pattern.compile("SomeRegexPattern"));
fail("replaced accepted patterns modifiable?");
} catch (UnsupportedOperationException uoe) {
// Expected result
}
try {
acceptedPatternSet.clear();
fail ("accepted patterns modifiable?");
} catch(UnsupportedOperationException uoe) {
fail("accepted patterns modifiable?");
} catch (UnsupportedOperationException uoe) {
// Expected result
}
}
public void testDmiIsEnabled() {
DefaultAcceptedPatternsChecker checker = new DefaultAcceptedPatternsChecker(Boolean.TRUE.toString());
AcceptedPatternsChecker.IsAccepted accepted = checker.isAccepted("action:myAction!save");
assertTrue("dmi isn't accepted", accepted.isAccepted());
}
}
@@ -48,6 +48,7 @@ public class DateRangeValidatorTest extends XWorkTestCase {
HashMap<String, Object> params = new HashMap<>();
params.put("date", date.getTime());
context.put(ActionContext.PARAMETERS, HttpParameters.create(params).build());
context.put(ActionContext.LOCALE, Locale.US); // Force US Locale for date conversion tests on JDK9+
ActionProxy proxy = actionProxyFactory.createActionProxy("", MockConfigurationProvider.VALIDATION_ACTION_NAME, null, context);
proxy.execute();
@@ -38,6 +38,7 @@ public class VisitorFieldValidatorTest extends XWorkTestCase {
public void setUp() throws Exception {
super.setUp();
ActionContext.getContext().put(ActionContext.LOCALE, Locale.US); // Force US Locale for date conversion tests on JDK9+
action = container.inject(VisitorValidatorTestAction.class);
TestBean bean = action.getBean();
@@ -20,6 +20,7 @@ package org.apache.struts2.dispatcher;
import com.mockobjects.dynamic.C;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.config.Configuration;
@@ -36,6 +37,7 @@ import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
import org.apache.struts2.util.ObjectFactoryDestroyable;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockServletContext;
import javax.servlet.http.HttpServletRequest;
@@ -374,6 +376,207 @@ public class DispatcherTest extends StrutsInternalTestCase {
assertTrue("Modified Dispatcher devMode state not true ?", du2.isDevMode());
}
public void testGetLocale_With_DefaultLocale_FromConfiguration() throws Exception {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
HttpServletRequest request = (HttpServletRequest) mock.proxy();
HttpServletResponse response = new MockHttpServletResponse();
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
// Not setting a Struts Locale here, so we should receive the default "de_DE" from the test configuration.
}});
// When
testDispatcher.prepare(request, response);
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
// Then
assertEquals(Locale.GERMANY, contextMap.get(ActionContext.LOCALE)); // Expect the Dispatcher defaultLocale value "de_DE" from the test configuration.
mock.verify();
}
public void testGetLocale_With_DefaultLocale_fr_CA() throws Exception {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
HttpServletRequest request = (HttpServletRequest) mock.proxy();
HttpServletResponse response = new MockHttpServletResponse();
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
put(StrutsConstants.STRUTS_LOCALE, Locale.CANADA_FRENCH.toString()); // Set the Dispatcher defaultLocale to fr_CA.
}});
// When
testDispatcher.prepare(request, response);
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
// Then
assertEquals(Locale.CANADA_FRENCH, contextMap.get(ActionContext.LOCALE)); // Expect the Dispatcher defaultLocale value.
mock.verify();
}
public void testGetLocale_With_BadDefaultLocale_RequestLocale_en_UK() throws Exception {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
mock.expectAndReturn("getLocale", Locale.UK); // From Dispatcher prepare().
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
mock.expectAndReturn("getLocale", Locale.UK); // From createTestContextMap().
HttpServletRequest request = (HttpServletRequest) mock.proxy();
HttpServletResponse response = new MockHttpServletResponse();
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
put(StrutsConstants.STRUTS_LOCALE, "This_is_not_a_valid_Locale_string"); // Set Dispatcher defaultLocale to an invalid value.
}});
// When
testDispatcher.prepare(request, response);
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
// Then
assertEquals(Locale.UK, contextMap.get(ActionContext.LOCALE)); // Expect the request set value from Mock.
mock.verify();
}
public void testGetLocale_With_BadDefaultLocale_And_RuntimeException() throws Exception {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
mock.expectAndReturn("getLocale", Locale.UK); // From Dispatcher prepare().
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
mock.expectAndThrow("getLocale", new IllegalStateException("Test theoretical state preventing HTTP Request Locale access")); // From createTestContextMap().
HttpServletRequest request = (HttpServletRequest) mock.proxy();
HttpServletResponse response = new MockHttpServletResponse();
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
put(StrutsConstants.STRUTS_LOCALE, "This_is_not_a_valid_Locale_string"); // Set the Dispatcher defaultLocale to an invalid value.
}});
// When
testDispatcher.prepare(request, response);
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
// Then
assertEquals(Locale.getDefault(), contextMap.get(ActionContext.LOCALE)); // Expect the system default value, when BOTH Dispatcher default Locale AND request access fail.
mock.verify();
}
public void testGetLocale_With_NullDefaultLocale() throws Exception {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From Dispatcher prepare().
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From createTestContextMap().
HttpServletRequest request = (HttpServletRequest) mock.proxy();
HttpServletResponse response = new MockHttpServletResponse();
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
// Attempting to set StrutsConstants.STRUTS_LOCALE to null here via parameters causes an NPE.
}});
testDispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
// When
testDispatcher.prepare(request, response);
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
// Then
assertEquals(Locale.CANADA_FRENCH, contextMap.get(ActionContext.LOCALE)); // Expect the request set value from Mock.
mock.verify();
}
public void testGetLocale_With_NullDefaultLocale_And_RuntimeException() throws Exception {
// Given
Mock mock = new Mock(HttpServletRequest.class);
MockHttpSession mockHttpSession = new MockHttpSession();
mock.expectAndReturn("getCharacterEncoding", "utf-8"); // From Dispatcher prepare().
mock.expectAndReturn("getHeader", "X-Requested-With", ""); // From Dispatcher prepare().
mock.expectAndReturn("getLocale", Locale.CANADA_FRENCH); // From Dispatcher prepare().
mock.expectAndReturn("getParameterMap", new HashMap<String, Object>()); // From Dispatcher prepare().
mock.expectAndReturn("getSession", false, mockHttpSession); // From Dispatcher prepare().
mock.expectAndReturn("getSession", true, mockHttpSession); // From createTestContextMap().
mock.expectAndThrow("getLocale", new IllegalStateException("Test some theoretical state preventing HTTP Request Locale access")); // From createTestContextMap().
HttpServletRequest request = (HttpServletRequest) mock.proxy();
HttpServletResponse response = new MockHttpServletResponse();
Dispatcher testDispatcher = initDispatcher(new HashMap<String, String>() {{
put(StrutsConstants.STRUTS_I18N_ENCODING, "utf-8");
// Attempting to set StrutsConstants.STRUTS_LOCALE to null via parameters causes an NPE.
}});
testDispatcher.setDefaultLocale(null); // Force a null Struts default locale, otherwise we receive the default "de_DE" from the test configuration.
// When
testDispatcher.prepare(request, response);
Map<String, Object> contextMap = createTestContextMap(testDispatcher, request, response);
// Then
assertEquals(Locale.getDefault(), contextMap.get(ActionContext.LOCALE)); // Expect the system default value when Mock request access fails.
mock.verify();
}
/**
* Create a test context Map from a Dispatcher instance.
*
* The method directly calls getParameterMap() and getSession(true) on the HttpServletRequest.
*
* The method indirectly calls getLocale(request) on the Dispatcher instance, allowing a test of that code path.
* The derived Struts Dispatcher Locale can be retrieved from the Map afterwards.
*
* @param dispatcher
* @param request
* @param response
* @return
*/
protected static Map<String, Object> createTestContextMap(Dispatcher dispatcher,
HttpServletRequest request, HttpServletResponse response) {
if (dispatcher == null) {
throw new IllegalArgumentException("Cannot create a test ContextMap from a null Dispatcher");
}
if (request == null) {
throw new IllegalArgumentException("Cannot create a test ContextMap from a null HttpServletRequest");
}
if (response == null) {
throw new IllegalArgumentException("Cannot create a test ContextMap from a null HttpServletResponse");
}
return dispatcher.createContextMap(new RequestMap(request),
HttpParameters.create(request.getParameterMap()).build(),
new SessionMap(request),
new ApplicationMap(request.getSession(true).getServletContext()),
request,
response);
}
class InternalConfigurationManager extends ConfigurationManager {
public boolean destroyConfiguration = false;
@@ -40,6 +40,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -244,7 +245,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
public void testNoContentMultipartRequest() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding("text/html");
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data");
req.setContent(null); // there is no content
@@ -265,7 +266,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
public void testSuccessUploadOfATextFileMultipartRequest() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding("text/html");
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
@@ -322,7 +323,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
final String endline = "\r\n";
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding("text/html");
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("POST");
req.addHeader("Content-type", "multipart/form-data; boundary=" + bondary);
StringBuilder content = new StringBuilder(128);
@@ -371,7 +372,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
public void testMultipartRequestLocalizedError() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding("text/html");
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
@@ -121,6 +121,20 @@ public class SetTagTest extends AbstractUITagTest {
assertEquals(beginEndSpaceString, context.get("foo"));
}
public void testEmptyBody() throws JspException {
StrutsMockBodyContent mockBodyContent;
String variableName = "foo";
tag.setName(variableName);
tag.setValue(null);
mockBodyContent = new StrutsMockBodyContent(new MockJspWriter());
String emptyBody = "";
mockBodyContent.setString(emptyBody);
tag.setBodyContent(mockBodyContent);
tag.doStartTag();
tag.doEndTag();
assertEquals(emptyBody, context.get(variableName));
}
protected void setUp() throws Exception {
super.setUp();
+1 -1
View File
@@ -97,7 +97,7 @@
</action>
<action name="aliasTest" class="com.opensymphony.xwork2.SimpleAction">
<param name="aliases">#{ "aliasSource" : "aliasDest", "bar":"baz" }</param>
<param name="aliases">#{ "aliasSource" : "aliasDest", "bar":"baz", "notExisting":"blah" }</param>
<interceptor-ref name="params"/>
<interceptor-ref name="alias"/>
<result name="success" type="mock" />
+2 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -52,7 +52,7 @@
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
<version>5.4.3.Final</version>
<scope>test</scope>
</dependency>
<dependency>
+2 -2
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-cdi-plugin</artifactId>
@@ -47,7 +47,7 @@
</dependency>
<dependency>
<groupId>org.jboss.weld</groupId>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se</artifactId>
<scope>test</scope>
</dependency>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-config-browser-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-convention-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-dwr-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-embeddedjsp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-gxp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-jasperreports-plugin</artifactId>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-javatemplates-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-jfreechart-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-json-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-junit-plugin</artifactId>
+24 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-osgi-plugin</artifactId>
@@ -71,6 +71,29 @@
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- The Servlet API mocks in Spring Framework 4.x only supports Servlet 3.0 and higher.
This is only necessary in tests-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -33,26 +33,31 @@ import java.util.Hashtable;
*/
public class BundleClassLoaderInterface implements ClassLoaderInterface {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return DefaultBundleAccessor.getInstance().loadClass(name);
}
@Override
public URL getResource(String name) {
return DefaultBundleAccessor.getInstance().loadResource(name, true);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
Collection<URL> coll = DefaultBundleAccessor.getInstance().loadResources(name, true);
if (coll == null){
if (coll == null) {
return new Hashtable<Object, URL>().elements();
}
return Collections.enumeration(DefaultBundleAccessor.getInstance().loadResources(name, true));
return Collections.enumeration(coll);
}
@Override
public InputStream getResourceAsStream(String name) throws IOException {
return DefaultBundleAccessor.getInstance().loadResourceAsStream(name);
}
@Override
public ClassLoaderInterface getParent() {
return null;
}
@@ -42,18 +42,26 @@ public class BundleFreemarkerManager extends FreemarkerManager {
private static final Logger LOG = LogManager.getLogger(BundleFreemarkerManager.class);
@Override
protected TemplateLoader createTemplateLoader(ServletContext servletContext, String templatePath) {
TemplateLoader templatePathLoader = null;
if (servletContext == null) {
throw new IllegalArgumentException("Unable to create a template loader using a null servlet context"); // Better than a NPE.
}
try {
if (templatePath.startsWith("class://")) {
if (templatePath != null && templatePath.startsWith("class://")) {
// substring(7) is intentional as we "reuse" the last slash
templatePathLoader = new ClassTemplateLoader(getClass(), templatePath.substring(7));
} else if (templatePath.startsWith("file://")) {
} else if (templatePath != null && templatePath.startsWith("file://")) {
templatePathLoader = new FileTemplateLoader(new File(URI.create(templatePath)));
} else {
LOG.warn("The provided template path specified: [{}] does not start with expected path protocol (class: or file:). " +
"The template loader will only look in standard locations", templatePath);
}
} catch (IOException e) {
LOG.error("Invalid template path specified: " + e.getMessage(), e);
LOG.error("Invalid template path specified: [{}]", e.getMessage(), e);
}
// presume that most apps will require the class and webapp template loader
@@ -24,9 +24,11 @@ import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -45,42 +47,121 @@ public class BundlePackageLoader implements PackageLoader {
private static final Logger LOG = LogManager.getLogger(BundlePackageLoader.class);
private Container contextContainer = null;
@Deprecated
@Override
public List<PackageConfig> loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory,
FileManagerFactory fileManagerFactory, Map<String, PackageConfig> pkgConfigs) throws ConfigurationException {
if (pkgConfigs == null) {
throw new IllegalArgumentException("Cannot load packages from a null package configuration"); // Better than a NPE.
}
Configuration config = new DefaultConfiguration("struts.xml");
LOG.trace("LoadPackages - After config constructed. Before BundleConfigurationProvider constructed");
BundleConfigurationProvider prov = new BundleConfigurationProvider("struts.xml", bundle, bundleContext);
LOG.trace("LoadPackages - After BundleConfigurationProvider constructed. Before config.addPackageConfig loop");
for (PackageConfig pkg : pkgConfigs.values()) {
config.addPackageConfig(pkg.getName(), pkg);
}
LOG.trace("LoadPackages - After config.addPackageConfig loop. Before prov.setObjectFactory()");
prov.setObjectFactory(objectFactory);
prov.setFileManagerFactory(fileManagerFactory);
if (fileManagerFactory == null || fileManagerFactory.getFileManager() == null) {
LOG.warn("LoadPackages - FileManagerFactory parameter is null or produces a null FileManager, replacing with a new DefaultFileManagerFactory instance");
final DefaultFileManagerFactory defaultFileManagerFactory = new DefaultFileManagerFactory();
final Container container = getContextContainer();
if (container == null) {
LOG.warn("LoadPackages - Config Container is null. May cause a NPE to be thrown");
} else {
container.inject(defaultFileManagerFactory); // Apply configuration (including the container reference) to the DefaultFileManagerFactory instance.
}
prov.setFileManagerFactory(defaultFileManagerFactory);
} else {
prov.setFileManagerFactory(fileManagerFactory);
}
LOG.trace("LoadPackages - After prov.setFileManagerFactory(). Before init()");
prov.init(config);
LOG.trace("LoadPackages - After prov.init(). Before loadPackages()");
prov.loadPackages();
List<PackageConfig> list = new ArrayList<PackageConfig>(config.getPackageConfigs().values());
LOG.trace("LoadPackages - After prov.loadPackages(). Before config.getPackageConfigs().values()");
List<PackageConfig> list = new ArrayList<>(config.getPackageConfigs().values());
LOG.trace("LoadPackages - After config.getPackageConfigs(). Before pkgConfigs.values()");
list.removeAll(pkgConfigs.values());
return list;
}
static class BundleConfigurationProvider extends XmlConfigurationProvider {
@Override
public List<PackageConfig> loadPackages(Container container, Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory,
FileManagerFactory fileManagerFactory, Map<String, PackageConfig> pkgConfigs) throws ConfigurationException {
setContextContainer(container); // Prepare Container state for standard signature call.
return loadPackages(bundle, bundleContext, objectFactory, fileManagerFactory, pkgConfigs);
}
/**
* Method to get the ActionContext (or other) Container instance required for the FileManagerFactory to work.
*
* Cheap workaround for keeping the deprecated signature version of loadPackages() around. Used by the new signature and can
* be used standalone with the old signature (developer's choice).
*
* @return The Container associated with this PackageLoader.
*/
protected Container getContextContainer() {
return contextContainer;
}
/**
* Method to set the ActionContext (or other) Container instance required for the FileManagerFactory to work.
*
* Cheap workaround for keeping the deprecated signature version of loadPackages() around. Used by the new signature and can
* be used standalone with the old signature (developer's choice).
*
* @param contextContainer The Container associated with this PackageLoader.
*/
protected void setContextContainer(Container contextContainer) {
this.contextContainer = contextContainer;
}
static class BundleConfigurationProvider extends StrutsXmlConfigurationProvider {
private Bundle bundle;
private BundleContext bundleContext;
public BundleConfigurationProvider(String filename, Bundle bundle, BundleContext bundleContext) {
super(filename, false);
super(filename, false, null); // Currently no dependency on ServletContext
this.bundle = bundle;
this.bundleContext = bundleContext;
}
public BundleConfigurationProvider(String filename) {
super(filename);
super(filename, false, null); // Currently no dependency on ServletContext
}
@Override
protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
if (bundle == null) {
throw new IllegalStateException("Unable to get configuration URLs, current Bundle reference is null"); // Better than a NPE.
}
Enumeration<URL> e = bundle.getResources("struts.xml");
return e.hasMoreElements() ? new EnumeratorIterator<URL>(e) : null;
return e.hasMoreElements() ? new EnumeratorIterator<>(e) : null;
}
}
@@ -91,14 +172,17 @@ public class BundlePackageLoader implements PackageLoader {
this.e = e;
}
@Override
public boolean hasNext() {
return e.hasMoreElements();
}
@Override
public E next() {
return e.nextElement();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@@ -54,46 +54,62 @@ public class DefaultBundleAccessor implements BundleAccessor {
private Map<Bundle, Set<String>> packagesByBundle = new HashMap<>();
private OsgiHost osgiHost;
/**
* This is a "defective" Singleton, possibly due to framework initialization requirements.
*
* Do NOT call this constructor directly, use {@link getInstance instead}.
*/
public DefaultBundleAccessor() {
self = this;
if (self == null) {
self = this; // First instantiation
} else {
// Copy current "singleton" to avoid loosing state, and then replace the old instance.
this.bundleContext = self.bundleContext;
this.packageToBundle = self.packageToBundle;
this.packagesByBundle = self.packagesByBundle;
this.osgiHost = self.osgiHost;
self = this;
}
}
public static DefaultBundleAccessor getInstance() {
return self;
}
@Override
public Object getService(ServiceReference ref) {
return bundleContext != null ? bundleContext.getService(ref) : null;
}
@Override
public ServiceReference getServiceReference(String className) {
return bundleContext != null ? bundleContext.getServiceReference(className) : null;
}
@Override
public ServiceReference[] getAllServiceReferences(String className) {
if (bundleContext != null) {
try {
return bundleContext.getServiceReferences(className, null);
} catch (InvalidSyntaxException e) {
//cannot happen we are passing null as the param
if (LOG.isErrorEnabled()) {
LOG.error("Invalid syntax for service lookup", e);
}
LOG.error("Invalid syntax for service lookup", e);
}
}
return null;
}
@Override
public ServiceReference[] getServiceReferences(String className, String params) throws InvalidSyntaxException {
return bundleContext != null ? bundleContext.getServiceReferences(className, params) : null;
}
/**
* Add as Bundle -&gt; Package mapping
* Add as Bundle -&gt; Package mapping
* @param bundle the bundle where the package was loaded from
* @param packageName the anme of the loaded package
* @param packageName the name of the loaded package
*/
@Override
public void addPackageFromBundle(Bundle bundle, String packageName) {
this.packageToBundle.put(packageName, bundle.getSymbolicName());
Set<String> pkgs = packagesByBundle.get(bundle);
@@ -104,6 +120,7 @@ public class DefaultBundleAccessor implements BundleAccessor {
pkgs.add(packageName);
}
@Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
Bundle bundle = getCurrentBundle();
if (bundle != null) {
@@ -117,6 +134,11 @@ public class DefaultBundleAccessor implements BundleAccessor {
private Bundle getCurrentBundle() {
ActionContext ctx = ActionContext.getContext();
if (ctx == null) {
throw new IllegalStateException("Unable to get current bundle, ActionContext is null"); // Better than a NPE.
}
String bundleName = (String) ctx.get(CURRENT_BUNDLE_NAME);
if (bundleName == null) {
ActionInvocation inv = ctx.getActionInvocation();
@@ -146,10 +168,10 @@ public class DefaultBundleAccessor implements BundleAccessor {
}
return resources;
}
return null;
}
@Override
public URL loadResourceFromAllBundles(String name) throws IOException {
for (Map.Entry<String, Bundle> entry : osgiHost.getActiveBundles().entrySet()) {
Enumeration e = entry.getValue().getResources(name);
@@ -157,10 +179,10 @@ public class DefaultBundleAccessor implements BundleAccessor {
return (URL) e.nextElement();
}
}
return null;
}
@Override
public InputStream loadResourceFromAllBundlesAsStream(String name) throws IOException {
URL url = loadResourceFromAllBundles(name);
if (url != null) {
@@ -180,21 +202,19 @@ public class DefaultBundleAccessor implements BundleAccessor {
try {
return translate ? OsgiUtil.translateBundleURLToJarURL(url, getCurrentBundle()) : url;
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Unable to translate bundle URL to jar URL", e);
}
LOG.error("Unable to translate bundle URL to jar URL", e);
return null;
}
}
return null;
}
@Override
public Set<String> getPackagesByBundle(Bundle bundle) {
return packagesByBundle.get(bundle);
}
@Override
public InputStream loadResourceAsStream(String name) throws IOException {
URL url = loadResource(name);
if (url != null) {
@@ -203,10 +223,12 @@ public class DefaultBundleAccessor implements BundleAccessor {
return null;
}
@Override
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
@Override
public void setOsgiHost(OsgiHost osgiHost) {
this.osgiHost = osgiHost;
}
@@ -23,62 +23,118 @@ import com.opensymphony.xwork2.config.PackageProvider;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.struts2.util.ObjectFactoryDestroyable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Map;
public class DelegatingObjectFactory extends ObjectFactory implements ObjectFactoryDestroyable {
private static final Logger LOG = LogManager.getLogger(DelegatingObjectFactory.class);
private ObjectFactory delegateObjectFactory;
private BundleAccessor bundleResourceLoader;
private OsgiConfigurationProvider osgiConfigurationProvider;
/**
* Create a DelegatingObjectFactory instance for constructing objects needed by the OSGi plugin.
*
* Note: Due to some Core components (e.g. DefaultTypeConverterCreator) calling on the object factory before the OSGi plugin can be initialized,
* we need something like a default delegate factory on construction. The Struts object factory would be the default delegate factory, but
* unfortunately the ObjectFactory hierarchy does not allow us to access the ancestor container or classloader. The best we can do is
* call the ancestor in the event no delegate has been set.
*/
public DelegatingObjectFactory() {
super();
LOG.trace("OSGi Plugin - DelegatingObjectFactory() constructor called.");
}
@Inject
public void setDelegateObjectFactory(@Inject Container container,
@Inject("struts.objectFactory.delegate") String delegate) {
LOG.trace("OSGi Plugin - setDelegateObjectFactory() call - Delegate: [{}], Container: [{}]", delegate, container);
if (delegate == null) {
delegate = "struts";
}
delegateObjectFactory = container.getInstance(ObjectFactory.class, delegate);
LOG.trace("OSGi Plugin - setDelegateObjectFactory() call - Result: [{}]", delegateObjectFactory);
}
@Inject
public void setBundleResourceLoader(BundleAccessor rl) {
LOG.trace("OSGi Plugin - Set BundleResourceLoader() call - BundleAccessor: [{}]", rl);
this.bundleResourceLoader = rl;
}
@Override
public boolean isNoArgConstructorRequired() {
if (delegateObjectFactory == null) {
throw new IllegalStateException("Cannot check if no-argument constructor required, delegate object factory is null"); // Better than a NPE.
}
return delegateObjectFactory.isNoArgConstructorRequired();
}
@Override
public Object buildBean(Class clazz, Map extraContext) throws Exception {
if (delegateObjectFactory == null) {
LOG.trace("OSGi Plugin - buildbean(Class, Map) call when delegateObjectFactory is NULL. NPE would result, calling ancestor (fallback)");
return super.buildBean(clazz, extraContext);
}
return delegateObjectFactory.buildBean(clazz, extraContext);
}
@Override
public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
if (delegateObjectFactory == null) {
LOG.trace("OSGi Plugin - buildbean(Class, Map, boolean) call when delegateObjectFactory is NULL. NPE would result, calling ancestor (fallback)");
return super.buildBean(className, extraContext, injectInternal);
}
try {
return delegateObjectFactory.buildBean(className, extraContext, injectInternal);
} catch (Exception e) {
if (bundleResourceLoader == null) {
throw new IllegalStateException("Cannot build bean, bundle resource loader is null"); // Better than a NPE.
}
Object object = bundleResourceLoader.loadClass(className).newInstance();
if (injectInternal)
if (injectInternal) {
injectInternalBeans(object);
}
return object;
}
}
@Override
public Class getClassInstance(String className) throws ClassNotFoundException {
if (delegateObjectFactory == null) {
LOG.trace("OSGi Plugin - buildbean(Class, Map, boolean) call when delegateObjectFactory is NULL. NPE would result, calling ancestor (fallback)");
return super.getClassInstance(className);
}
try {
return delegateObjectFactory.getClassInstance(className);
}
catch (Exception e) {
if (bundleResourceLoader == null) {
throw new IllegalStateException("Cannot get class instance, bundle resource loader is null"); // Better than a NPE.
}
return bundleResourceLoader.loadClass(className);
}
}
@Override
public void destroy() {
if (osgiConfigurationProvider != null) {
osgiConfigurationProvider.destroy();
@@ -87,6 +143,8 @@ public class DelegatingObjectFactory extends ObjectFactory implements ObjectFact
@Inject("osgi")
public void setOsgiConfigurationProvider(PackageProvider osgiConfigurationProvider) {
LOG.trace("OSGi Plugin - setOsgiConfigurationProvider() call - PackageProvider: [{}]", osgiConfigurationProvider);
this.osgiConfigurationProvider = (OsgiConfigurationProvider) osgiConfigurationProvider;
}
@@ -46,7 +46,7 @@ import java.util.Properties;
import java.util.Set;
/**
* Struts package provider that starts the OSGi container and deelgates package loading
* Struts package provider that starts the OSGi container and delegates package loading
*/
public class OsgiConfigurationProvider implements PackageProvider, BundleListener {
@@ -62,24 +62,57 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
private boolean bundlesChanged = false;
private ServletContext servletContext;
@Override
public void init(Configuration configuration) throws ConfigurationException {
if (servletContext == null) {
throw new IllegalStateException("ServletContext is null (may not have been injected). Unable to initialize"); // Better than a NPE.
}
osgiHost = (OsgiHost) servletContext.getAttribute(StrutsOsgiListener.OSGI_HOST);
if (osgiHost == null) {
throw new IllegalStateException("Cannot locate Struts OsgiHost in servlet context (null). Unable to initialize"); // Better than a NPE.
}
if (bundleAccessor == null) {
throw new IllegalStateException("BundleAccessor is null (may not have been injected). Unable to initialize"); // Better than a NPE.
}
bundleContext = osgiHost.getBundleContext();
bundleAccessor.setBundleContext(bundleContext);
bundleAccessor.setOsgiHost(osgiHost);
this.configuration = configuration;
if (bundleContext == null) {
LOG.warn("Struts BundleContext is null at initialization, check OsgiHost configuration");
}
if (configuration == null) {
LOG.warn("Struts OSGi configuration is null at initialization, check OsgiHost configuration");
}
if (bundleAccessor instanceof DefaultBundleAccessor) {
LOG.debug("Struts OSGi Bundle Accessor is a DefaultBundleAccessor or descendant");
} else {
LOG.debug("Struts OSGi Bundle Accessor is a non-standard BundleAccessor");
}
//this class loader interface can be used by other plugins to lookup resources
//from the bundles. A temporary class loader interface is set during other configuration
//loading as well
servletContext.setAttribute(ClassLoaderInterface.CLASS_LOADER_INTERFACE, new BundleClassLoaderInterface());
}
@Override
public synchronized void loadPackages() throws ConfigurationException {
if (LOG.isTraceEnabled())
LOG.trace("Loading packages from XML and Convention on startup");
LOG.trace("Loading packages from XML and Convention on startup");
//init action context
if (osgiHost == null) {
throw new IllegalStateException("Cannot load packages, Struts OsgiHost is null"); // Better than a NPE.
}
if (bundleContext == null) {
throw new IllegalStateException("Cannot load packages, Struts BundleContext is null"); // Better than a NPE.
}
//init action contect
ActionContext ctx = ActionContext.getContext();
if (ctx == null) {
ctx = createActionContext();
@@ -113,6 +146,19 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
* @param bundle the bundle
*/
protected void loadConfigFromBundle(Bundle bundle) {
if (bundle == null) {
throw new IllegalArgumentException("Cannot load configuration from a null bundle"); // Better than a NPE.
}
if (configuration == null) {
throw new IllegalStateException("Struts OSGi configuration is null. Cannot load bundle configuration"); // Better than a NPE.
}
if (bundleAccessor == null) {
LOG.warn("BundleAccessor is null (may not have been injected). May cause NPE to be thrown");
}
if (fileManagerFactory == null) {
LOG.warn("FileManagerFactory is null, FileManagerFactory may not have been set. May cause NPE to be thrown");
}
String bundleName = bundle.getSymbolicName();
LOG.debug("Loading packages from bundle [{}]", bundleName);
@@ -148,7 +194,7 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
conventionPackageProvider.loadPackages();
}
Set<String> packagesAfterLoading = new HashSet<String>(configuration.getPackageConfigNames());
Set<String> packagesAfterLoading = new HashSet<>(configuration.getPackageConfigNames());
packagesAfterLoading.removeAll(packagesBeforeLoading);
if (!packagesAfterLoading.isEmpty()) {
//add the new packages to the map of bundle -> package
@@ -175,28 +221,39 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
* @return true is struts2 enabled
*/
protected boolean shouldProcessBundle(Bundle bundle) {
if (bundle == null) {
throw new IllegalArgumentException("Cannot check if Struts OSGi Plugin should process a null bundle"); // Better than a NPE.
}
// Cast to String is required on JDK7
String strutsEnabled = (String) bundle.getHeaders().get(OsgiHost.OSGI_HEADER_STRUTS_ENABLED);
return "true".equalsIgnoreCase(strutsEnabled);
}
@Override
public synchronized boolean needsReload() {
return bundlesChanged;
}
@Inject
public void setObjectFactory(ObjectFactory factory) {
LOG.trace("OSGi ConfigurationProvider - setObjectFactory() called - ObjectFactory: [{}]", factory);
this.objectFactory = factory;
}
@Inject
public void setBundleAccessor(BundleAccessor acc) {
LOG.trace("OSGi ConfigurationProvider - setBundleAccessor() called - BundleAccessor: [{}]", acc);
this.bundleAccessor = acc;
}
@Inject
public void setVelocityManager(VelocityManager vm) {
LOG.trace("OSGi ConfigurationProvider - setVelocityManager() called - VelocityManager: [{}]", vm);
Properties props = new Properties();
props.setProperty("osgi.resource.loader.description", "OSGI bundle loader");
props.setProperty("osgi.resource.loader.class", VelocityBundleResourceLoader.class.getName());
@@ -206,11 +263,15 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
@Inject
public void setServletContext(ServletContext servletContext) {
LOG.trace("OSGi ConfigurationProvider - setServletContext() called - ServletContext: [{}]", servletContext);
this.servletContext = servletContext;
}
@Inject
public void setFileManagerFactory(FileManagerFactory fmFactory) {
LOG.trace("OSGi ConfigurationProvider - setFileManagerFactory() called - FileManagerFactory: [{}]", fmFactory);
this.fileManagerFactory = fmFactory;
}
@@ -229,8 +290,18 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
*
* @param bundleEvent the bundle event
*/
@Override
public void bundleChanged(BundleEvent bundleEvent) {
if (bundleEvent == null) {
throw new IllegalArgumentException("Cannot check if bundle changed for a null BundleEvent"); // Better than a NPE.
}
Bundle bundle = bundleEvent.getBundle();
if (bundle == null) {
throw new IllegalArgumentException("Cannot check if bundle changed for a null Bundle within the BundleEvent"); // Better than a NPE.
}
String bundleName = bundle.getSymbolicName();
if (bundleName != null && shouldProcessBundle(bundle)) {
switch (bundleEvent.getType()) {
@@ -251,9 +322,16 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene
* @param bundle the bundle that stopped
*/
protected void onBundleStopped(Bundle bundle) {
if (bundle == null) {
throw new IllegalArgumentException("Cannot process bundle stopped event on a null Bundle"); // Better than a NPE.
}
if (bundleAccessor == null) {
throw new IllegalStateException("BundleAccessor is null. Cannot process bundle stopped event"); // Better than a NPE.
}
Set<String> packages = bundleAccessor.getPackagesByBundle(bundle);
if (!packages.isEmpty()) {
if (LOG.isTraceEnabled()) {
if (LOG.isTraceEnabled()) { // Avoid packages join cost, except when trace level logging is enabled.
LOG.trace("The bundle [{}] has been stopped. The packages [{}] will be disabled", bundle.getSymbolicName(), StringUtils.join(packages, ","));
}
for (String packageName : packages) {
@@ -39,7 +39,7 @@ public class OsgiUtil {
*
* @return translated URL
*
* @throws MalformedURLException if url is malformed
* @throws MalformedURLException if URL is malformed
*/
public static URL translateBundleURLToJarURL(URL bundleUrl, Bundle bundle) throws MalformedURLException {
if (bundleUrl != null && "bundle".equalsIgnoreCase(bundleUrl.getProtocol())) {
@@ -54,8 +54,8 @@ public class OsgiUtil {
}
/**
* Calls getBean() on the passed object using refelection. Used on Spring context
* because they are loaded from bundles (in anothe class loader)
* Calls getBean() on the passed object using reflection. Used on Spring context
* because they are loaded from bundles (in another class loader)
*
* @param beanFactory bean factory
* @param beanId id of bean
@@ -74,8 +74,8 @@ public class OsgiUtil {
}
/**
* Calls containsBean on the passed object using refelection. Used on Spring context
* because they are loaded from bundles (in anothe class loader)
* Calls containsBean on the passed object using reflection. Used on Spring context
* because they are loaded from bundles (in another class loader)
*
* @param beanFactory bean factory
* @param beanId id of bean
@@ -92,4 +92,83 @@ public class OsgiUtil {
return false;
}
/**
* Attempt to generate an OSGi compatible Java version String from the System "java.version" property,
* with the form (where x is the major version number): 1.x for Java 8 and earlier, x.y for Java 9+.
* See <a href="https://openjdk.java.net/jeps/223">JEP 223: New Version-String Scheme</a> for details
* on version naming changes.
*
* @param systemJavaVersion a Java version string from System.getProperty("java.version") or equivalent.
* @return a String of the form (where x is the major version number): 1.x for Java 8 and earlier, x.y for Java 9+.
*/
public static String generateJavaVersionForSystemPackages(String systemJavaVersion) {
if (systemJavaVersion == null || systemJavaVersion.isEmpty()) {
throw new IllegalArgumentException("Cannot parse Java version from null or empty string");
} else {
String parsedResult;
final int dotIndex1 = systemJavaVersion.indexOf('.');
final int dotIndex2 = (dotIndex1 > 0 ? systemJavaVersion.indexOf('.', dotIndex1 + 1) : -1);
if (dotIndex1 > 0 && dotIndex2 > 0) {
parsedResult = systemJavaVersion.substring(0, dotIndex2); // Assuming Java 8 or older style, or Java 9+ with a minor or security update.
} else if (dotIndex1 > 0) {
parsedResult = systemJavaVersion; // Assuming a truncated Java 8 or older style, or Java 9+ with a minor or security update.
} else if (dotIndex1 == -1) {
final int minusIndex = systemJavaVersion.indexOf('-');
final int plusIndex = systemJavaVersion.indexOf('+');
if (minusIndex > 0) {
parsedResult = systemJavaVersion.substring(0, minusIndex); // Assuming Java 9+ early-access
} else if (plusIndex > 0) {
parsedResult = systemJavaVersion.substring(0, plusIndex); // Assuming Java 9+ java.runime.version or java.vm.version string
} else {
parsedResult = systemJavaVersion;
}
try {
final int firstNumber = Integer.parseInt(parsedResult);
if (firstNumber >= 9) {
parsedResult = parsedResult + ".0"; // Assuming Java 9+ and no minor or security update, append standard value.
} else {
throw new IllegalArgumentException("Single digit Java version string less than 9 (nonsense)");
}
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot parse Java version string (probable non-numeric start)", ex);
}
} else {
throw new IllegalArgumentException("Cannot parse Java version from a string starting with a '.'");
}
return parsedResult;
}
}
/**
* Attempt to generate an OSGi compatible Java SE system package version String from the System "java.version" property,
* with the form (where x or xx is the major version number): 0.0.0.JavaSE_001_00x for Java 8 and earlier (e.g. 0.0.0.JavaSE_001_008),
* 0.0.0.JavaSE_0xx for Java 9+ (e.g. 0.0.0.JavaSE_009 for Java 9, 0.0.0.JavaSE_011 for Java 11).
*
* @param systemJavaVersion a Java version string from System.getProperty("java.version") or equivalent.
* @return a String of the form (where x or xx is the major version number): JavaSE_001_00x for Java 8 and earlier, JavaSE_0xx for Java 9+.
*/
public static String generateJava_SE_SystemPackageVersionString(String systemJavaVersion) {
final String javaVersionForSystemPackages = generateJavaVersionForSystemPackages(systemJavaVersion);
final int dotIndex = javaVersionForSystemPackages.indexOf('.');
if (dotIndex > 0) {
final String generatedResult;
try {
final String prefix = javaVersionForSystemPackages.substring(0, dotIndex);
final String suffix = javaVersionForSystemPackages.substring(dotIndex + 1);
final int firstNumber = Integer.parseInt(prefix);
final int secondNumber = Integer.parseInt(suffix);
if (firstNumber >= 9) {
generatedResult = String.format("0.0.0.JavaSE_%03d", firstNumber); // Assuming Java 9+
} else {
generatedResult = String.format("0.0.0.JavaSE_001_%03d", secondNumber); // Assuming Java 8 or earlier
}
return generatedResult;
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot parse Java version string (probable non-numeric start)", ex);
}
} else {
throw new IllegalArgumentException("Cannot parse Java version from a system packages string missing or starting with a '.'");
}
}
}
@@ -22,6 +22,7 @@ import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.inject.Container;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -33,8 +34,41 @@ import java.util.Map;
*/
public interface PackageLoader {
/**
* This method loads all of the package configurations for the specified Bundle and BundleContext.
*
* This method signature is retained for ensuring backwards-compatibility Interface-wise, but is Deprecated as it
* cannot provide the now-necessary Container reference.
*
* @param bundle The OSGi bundle.
* @param bundleContext The OSGi bundle BundleContext.
* @param objectFactory The ObjectFactory to create object instances.
* @param fileManagerFactory The FileManagerFactory to access files.
* @param map The Map of PackageConfigs (by String name).
* @return A List of PackageConfigs that contains the packages loaded for the bundle.
* @throws ConfigurationException When the package load operation fails due to configuration errors.
*/
@Deprecated
List<PackageConfig> loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory,
FileManagerFactory fileManagerFactory,
Map<String, PackageConfig> map) throws ConfigurationException;
/**
* This method loads all of the package configurations for the specified Bundle and BundleContext.
*
* Introduces a new loadPackages method signature including a Container reference. This is necessary as the FileManagerFactory depends on
* access to an initialized Container reference to function properly.
*
* @param container The Container from the ActionContext (or another context).
* @param bundle The OSGi bundle.
* @param bundleContext The OSGi bundle BundleContext.
* @param objectFactory The ObjectFactory to create object instances.
* @param fileManagerFactory The FileManagerFactory to access files.
* @param map The Map of PackageConfigs (by String name).
* @return A List of PackageConfigs that contains the packages loaded for the bundle.
* @throws ConfigurationException When the package load operation fails due to configuration errors.
*/
List<PackageConfig> loadPackages(Container container, Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory, FileManagerFactory fileManagerFactory,
Map<String, PackageConfig> map) throws ConfigurationException;
}
@@ -38,34 +38,47 @@ public class SpringOsgiObjectFactory extends ObjectFactory {
public SpringOsgiObjectFactory() {
}
@Override
public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
if (containsBean(className))
if (containsBean(className)) {
return getBean(className);
else {
} else {
Class clazz = ClassLoaderUtil.loadClass(className, SpringOsgiObjectFactory.class);
Object object = clazz.newInstance();
if (injectInternal)
if (injectInternal) {
injectInternalBeans(object);
}
return object;
}
}
@Override
public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
if (clazz == null) {
throw new IllegalArgumentException("Unable to build bean from a null Class");
}
return clazz.newInstance();
}
@Override
public Class getClassInstance(String className) throws ClassNotFoundException {
return containsBean(className) ? getBean(className).getClass() : ClassLoaderUtil.loadClass(className, SpringOsgiObjectFactory.class);
}
protected Object getBean(String beanName) {
if (bundleAccessor == null) {
throw new IllegalStateException("BundleAccessor is null (may not have been injected). Unable to get bean"); // Better than a NPE.
}
ServiceReference[] refs = bundleAccessor.getAllServiceReferences(SPRING_SERVICE_NAME);
if (refs != null) {
for (ServiceReference ref : refs) {
Object context = bundleAccessor.getService(ref);
if (OsgiUtil.containsBean(context, beanName))
if (OsgiUtil.containsBean(context, beanName)) {
return OsgiUtil.getBean(context, beanName);
}
}
}
@@ -73,12 +86,17 @@ public class SpringOsgiObjectFactory extends ObjectFactory {
}
protected boolean containsBean(String beanName) {
if (bundleAccessor == null) {
throw new IllegalStateException("BundleAccessor is null (may not have been injected). Unable to check if contains bean"); // Better than a NPE.
}
ServiceReference[] refs = bundleAccessor.getAllServiceReferences(SPRING_SERVICE_NAME);
if (refs != null) {
for (ServiceReference ref : refs) {
Object context = bundleAccessor.getService(ref);
if (OsgiUtil.containsBean(context, beanName))
if (OsgiUtil.containsBean(context, beanName)) {
return true;
}
}
}
@@ -28,7 +28,7 @@ import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* ServletContextListener that starts Osgi host
* ServletContextListener that starts OSGi host
*/
public class StrutsOsgiListener implements ServletContextListener {
@@ -39,26 +39,46 @@ public class StrutsOsgiListener implements ServletContextListener {
private OsgiHost osgiHost;
@Override
public void contextInitialized(ServletContextEvent sce) {
LOG.trace("StrutsOsgiListener attempting to start. ContextInitialized called. SCE: [{}]", sce);
if (sce == null) {
throw new StrutsException("ServletContextEvent is null. Cannot init OSGi platform!"); // Better than a NPE.
}
ServletContext servletContext = sce.getServletContext();
LOG.trace("StrutsOsgiListener attempting to start. ServletContext: [{}]", servletContext);
if (servletContext == null) {
throw new StrutsException("ServletContext is null within the ServletContextEvent. Cannot init OSGi platform!"); // Better than a NPE.
}
String platform = servletContext.getInitParameter(PLATFORM_KEY);
LOG.debug("Defined OSGi platform as [{}] via context-param [{}]", platform, PLATFORM_KEY);
osgiHost = OsgiHostFactory.createOsgiHost(platform);
try {
osgiHost = OsgiHostFactory.createOsgiHost(platform);
LOG.trace("StrutsOsgiListener attempting to start. OSGi Host constructed: [{}]", osgiHost);
} catch (Throwable t) {
throw new StrutsException("StrutsOsgiListener failed to create an OSGi Host!", t);
}
servletContext.setAttribute(OSGI_HOST, osgiHost);
try {
osgiHost.init(servletContext);
} catch (Exception e) {
throw new StrutsException("Cannot init OSGi platform!", e);
throw new StrutsException("StrutsOsgiListener failed to initialize the OSGi Host platform!", e);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
try {
osgiHost.destroy();
} catch (Exception e) {
throw new StrutsException("Cannot stop OSGi platform!", e);
throw new StrutsException("StrutsOsgiListener cannot stop the OSGi Host platform!", e);
}
}
@@ -18,9 +18,14 @@
*/
package org.apache.struts2.osgi.host;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate;
import com.opensymphony.xwork2.util.finder.ResourceFinder;
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.commons.lang3.StringUtils;
@@ -28,15 +33,18 @@ import org.apache.felix.main.AutoProcessor;
import org.apache.felix.shell.ShellService;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsException;
import org.apache.struts2.osgi.OsgiUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.util.tracker.ServiceTracker;
import javax.servlet.ServletContext;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
@@ -53,17 +61,30 @@ import java.util.regex.Pattern;
/**
* A base OsgiHost implementation
* <br>
* Servlet config params:
* <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>
*/
public abstract class BaseOsgiHost implements OsgiHost {
private static final Logger LOG = LogManager.getLogger(BaseOsgiHost.class);
protected static final Pattern versionPattern = Pattern.compile("([\\d])+[\\.-]");
protected static final Pattern VERSION_PATTERN = Pattern.compile("([\\d]+)(?:[\\.-]|$)");
protected static final String SCANNING_PACKAGE_INCLUDES = "scanning.package.includes";
protected static final String JRE_JAVA_SPECIFICATION_VERSION = "${jre-${java.specification.version}}";
protected static final String DETECT_JAVA_VERSION = "${detect.java.version}";
/**
* @deprecated use {@link #VERSION_PATTERN} instead
*/
@Deprecated
protected static final Pattern versionPattern = VERSION_PATTERN;
protected ServletContext servletContext;
@Override
public abstract void init(ServletContext servletContext);
@Override
public abstract void destroy() throws Exception;
/**
@@ -72,10 +93,13 @@ public abstract class BaseOsgiHost implements OsgiHost {
*
* @return map with bundles
*/
@Override
public abstract Map<String, Bundle> getBundles();
@Override
public abstract Map<String, Bundle> getActiveBundles();
@Override
public abstract BundleContext getBundleContext();
protected abstract void addSpringOSGiSupport();
@@ -88,12 +112,15 @@ public abstract class BaseOsgiHost implements OsgiHost {
* @return param from the ServletContext, returning the default value if the param is not set
*/
protected String getServletContextParam(String paramName, String defaultValue) {
if (this.servletContext == null) {
throw new IllegalStateException("OSGi Host servlet context is null!");
}
return StringUtils.defaultString(this.servletContext.getInitParameter(paramName), defaultValue);
}
protected void addAutoStartBundles(Properties configProps) {
//starts system bundles in level 1
List<String> bundleJarsLevel1 = new ArrayList<String>();
List<String> bundleJarsLevel1 = new ArrayList<>();
bundleJarsLevel1.add(getJarUrl(ShellService.class));
bundleJarsLevel1.add(getJarUrl(ServiceTracker.class));
@@ -124,7 +151,7 @@ public abstract class BaseOsgiHost implements OsgiHost {
* @return a list of directories under a directory whose name is a number
*/
protected Map<String, String> getRunLevelDirs(String dir) {
Map<String, String> dirs = new HashMap<String, String>();
Map<String, String> dirs = new HashMap<>();
try {
ResourceFinder finder = new ResourceFinder();
URL url = finder.find("bundles");
@@ -132,6 +159,7 @@ public abstract class BaseOsgiHost implements OsgiHost {
if ("file".equals(url.getProtocol())) {
File bundlesDir = new File(url.toURI());
String[] runLevelDirs = bundlesDir.list(new FilenameFilter() {
@Override
public boolean accept(File file, String name) {
try {
return file.isDirectory() && Integer.valueOf(name) > 0;
@@ -151,7 +179,7 @@ public abstract class BaseOsgiHost implements OsgiHost {
LOG.debug("No run level directories found under the [{}] directory", dir);
}
} else {
LOG.warn("Unable to read [{}] directory", dir);
LOG.warn("Unable to read [{}] directory. Protocol [{}] is not supported (try exploded WAR files)", dir, url.getProtocol());
}
} else {
LOG.warn("The [{}] directory was not found", dir);
@@ -163,7 +191,7 @@ public abstract class BaseOsgiHost implements OsgiHost {
}
protected List<String> getBundlesInDir(String dir) {
List<String> bundleJars = new ArrayList<String>();
List<String> bundleJars = new ArrayList<>();
try {
ResourceFinder finder = new ResourceFinder();
URL url = finder.find(dir);
@@ -171,6 +199,7 @@ public abstract class BaseOsgiHost implements OsgiHost {
if ("file".equals(url.getProtocol())) {
File bundlesDir = new File(url.toURI());
File[] bundles = bundlesDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file, String name) {
return StringUtils.endsWith(name, ".jar");
}
@@ -188,7 +217,7 @@ public abstract class BaseOsgiHost implements OsgiHost {
LOG.debug("No bundles found under the [{}] directory", dir);
}
} else {
LOG.warn("Unable to read [{}] directory", dir);
LOG.warn("Unable to read [{}] directory. Protocol [{}] is not supported (try exploded WAR files)", dir, url.getProtocol());
}
} else {
LOG.warn("The [{}] directory was not found", dir);
@@ -206,26 +235,88 @@ public abstract class BaseOsgiHost implements OsgiHost {
return loc.toString();
}
/**
* Replace all instances of {@link JRE_JAVA_SPECIFICATION_VERSION}, 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 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 DETECT_JAVA_VERSION} in your properties file.
*
* @param properties OSGi properties for which the {@link Constants.FRAMEWORK_SYSTEMPACKAGES} property's values
* substrings {@link JRE_JAVA_SPECIFICATION_VERSION} 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 replaceSystemPackages(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 system packages using a null properties reference");
}
//${jre-${java.specification.version}}
String systemPackages = (String) properties.get(Constants.FRAMEWORK_SYSTEMPACKAGES);
String jreVersion = "jre-" + System.getProperty("java.version").substring(0, 3);
systemPackages = systemPackages.replace("${jre-${java.specification.version}}", (String) properties.get(jreVersion));
properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, 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 = jreJavaSpecificationVersionSubstitution.replace(DETECT_JAVA_VERSION, OsgiUtil.generateJava_SE_SystemPackageVersionString(systemJavaVersion)).trim();
systemPackages = systemPackages.replace(JRE_JAVA_SPECIFICATION_VERSION, jreJavaSpecificationVersionSubstitution);
properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages);
} else {
LOG.warn("Properties property [{}] is null or empty. Unable to replace JRE system packages. JRE system packages will be cleared", jreJavaSpecificationVersion);
systemPackages = systemPackages.replace(JRE_JAVA_SPECIFICATION_VERSION, "");
properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages);
}
} else {
LOG.warn("System property [{}] is null or empty. Unable to replace JRE system packages", "java.version");
}
LOG.debug("OSGi System Packages (after replacement): [{}]", systemPackages);
} else {
LOG.warn("Unable to replace JRE system packages. Properties required key [{}] is missing or empty",
Constants.FRAMEWORK_SYSTEMPACKAGES);
}
}
/*
Find subpackages of the packages defined in the property file and export them
/**
* Find sub-packages of the packages defined in the property file and export them
*
* @param strutsConfigProps Struts-OSGi configuration properties containing the {@link SCANNING_PACKAGE_INCLUDES} property
* containing comma-separated top-level package values.
* @param configProps OSGi configuration properties for which the {@link Constants.FRAMEWORK_SYSTEMPACKAGES} property's
* value will have the sub-packages of strutsConfigProps appended to it.
* If no {@link Constants.FRAMEWORK_SYSTEMPACKAGES} property exists, and the exported packages is non-empty,
* then one is created that will contain the sub-packages of strutsConfigProps.
*/
protected void addExportedPackages(Properties strutsConfigProps, Properties configProps) {
String[] rootPackages = StringUtils.split((String) strutsConfigProps.get("scanning.package.includes"), ",");
if (strutsConfigProps == null) {
throw new IllegalArgumentException("Cannot add exported packages using a null struts config properties reference");
}
LOG.debug(" scanning.package.includes lookup returns: [{}]", (String) strutsConfigProps.get(SCANNING_PACKAGE_INCLUDES));
String[] rootPackages = StringUtils.split((String) strutsConfigProps.get(SCANNING_PACKAGE_INCLUDES), ",");
ResourceFinder finder = new ResourceFinder(StringUtils.EMPTY);
List<String> exportedPackages = new ArrayList<String>();
List<String> exportedPackages = new ArrayList<>();
if (rootPackages == null || rootPackages.length == 0) {
LOG.warn("Struts config roperties required key [{}] is missing or empty. No exported packages are available to add", SCANNING_PACKAGE_INCLUDES);
return;
}
//build a list of subpackages
for (String rootPackage : rootPackages) {
LOG.debug(" Looking for root package: [{}] ", rootPackage);
try {
String version = null;
if (rootPackage.indexOf(";") > 0) {
@@ -234,6 +325,9 @@ public abstract class BaseOsgiHost implements OsgiHost {
version = splitted[1];
}
Map<URL, Set<String>> subpackagesMap = finder.findPackagesMap(StringUtils.replace(rootPackage.trim(), ".", "/"));
LOG.debug(" Trimmed package map for: [{}] has size: [{}]", rootPackage.trim(), subpackagesMap.size());
for (Map.Entry<URL, Set<String>> entry : subpackagesMap.entrySet()) {
URL url = entry.getKey();
Set<String> packages = entry.getValue();
@@ -241,12 +335,20 @@ public abstract class BaseOsgiHost implements OsgiHost {
//get version if not set
if (StringUtils.isBlank(version)) {
version = getVersion(url);
LOG.debug(" Version was null. Retrieved version: [{}] for [{}]", version, (url != null ? url.toString() : null) );
}
if (packages != null) {
LOG.debug(" Subpackages size: [{}]", packages.size());
for (String subpackage : packages) {
exportedPackages.add(subpackage + "; version=" + version);
LOG.trace(" Adding subppackage: [{}; version=\"{}\"]", subpackage, version);
exportedPackages.add(subpackage + "; version=\"" + version + "\"");
}
} else {
LOG.debug(" Subpackages is null");
}
}
} catch (IOException e) {
@@ -255,10 +357,20 @@ public abstract class BaseOsgiHost implements OsgiHost {
}
//make a string with the exported packages and add it to the system properties
if (!exportedPackages.isEmpty()) {
if (!exportedPackages.isEmpty() && configProps != null) {
String systemPackages = (String) configProps.get(Constants.FRAMEWORK_SYSTEMPACKAGES);
systemPackages = StringUtils.removeEnd(systemPackages, ",") + "," + StringUtils.join(exportedPackages, ",");
if (systemPackages == null || systemPackages.isEmpty()) {
LOG.warn("Config properties required key [{}] is missing or empty. Only the exported packages will be present",
Constants.FRAMEWORK_SYSTEMPACKAGES);
systemPackages = StringUtils.join(exportedPackages, ",");
} else {
systemPackages = StringUtils.removeEnd(systemPackages, ",") + "," + StringUtils.join(exportedPackages, ",");
}
configProps.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages);
LOG.debug("Adding exported framework packages: [{}]", systemPackages);
} else {
LOG.warn("NOT adding any exported framework packages. No exported packages or config props is null");
}
}
@@ -268,21 +380,71 @@ public abstract class BaseOsgiHost implements OsgiHost {
*/
protected String getVersion(URL url) {
if ("jar".equals(url.getProtocol())) {
JarFile jarFile = null;
try {
FileManager fileManager = ServletActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager();
JarFile jarFile = new JarFile(new File(fileManager.normalizeToFileProtocol(url).toURI()));
ActionContext actionContext = null;
FileManagerFactory fileManagerFactory = null;
FileManager fileManager = null;
actionContext = ServletActionContext.getContext();
if (actionContext == null) {
LOG.warn("ActionContext is null. Cannot load FileManagerFactory from it");
}
if (actionContext != null) {
fileManagerFactory = actionContext.getInstance(FileManagerFactory.class);
}
if (fileManagerFactory == null) {
LOG.warn("FileManagerFactory is null in ActionContext. Cannot load FileManager from it");
} else {
fileManager = fileManagerFactory.getFileManager();
}
if (fileManager == null) {
if (fileManagerFactory != null) {
LOG.debug("FileManager is null in FileManagerFactory. Using a DefaultFileManager");
} else {
LOG.debug("Using a DefaultFileManager");
}
fileManager = new DefaultFileManager();
}
jarFile = new JarFile(new File(fileManager.normalizeToFileProtocol(url).toURI()));
Manifest manifest = jarFile.getManifest();
String jarFileName = jarFile.getName();
if (jarFileName != null) {
// Strip extraneous file path elements to limit the string to the JAR name itself, if possible.
int lastExclamationIndex = jarFileName.lastIndexOf("!");
if (lastExclamationIndex != -1) {
jarFileName = jarFileName.substring(0, lastExclamationIndex);
}
if (jarFileName.toLowerCase().endsWith(".jar")) {
int lastPathSeparatorIndex = jarFileName.lastIndexOf(File.separator);
if (lastPathSeparatorIndex != -1) {
jarFileName = jarFileName.substring(lastPathSeparatorIndex + 1);
}
}
}
if (manifest != null) {
String version = manifest.getMainAttributes().getValue("Bundle-Version");
if (StringUtils.isNotBlank(version)) {
LOG.debug("Attempting to get bundle version for [{}] via its JAR manifest", url.toExternalForm());
return getVersionFromString(version);
} else {
//try to get the version from the file name
LOG.debug("Attempting to get bundle version for [{}] via its filename [{}]", url.toExternalForm(), jarFileName);
return getVersionFromString(jarFileName);
}
} else {
//try to get the version from the file name
return getVersionFromString(jarFile.getName());
LOG.debug("Attempting to get bundle version for [{}] via its filename [{}]", url.toExternalForm(), jarFileName);
return getVersionFromString(jarFileName);
}
} catch (Exception e) {
LOG.error("Unable to extract version from [{}], defaulting to '1.0.0'", url.toExternalForm());
} finally {
if (jarFile != null) {
try {
jarFile.close();
} catch (Exception ex) {}
}
}
}
return "1.0.0";
@@ -293,31 +455,78 @@ public abstract class BaseOsgiHost implements OsgiHost {
* @return Extracts numbers followed by "." or "-" from the string and joins them with "."
*/
protected String getVersionFromString(String str) {
Matcher matcher = versionPattern.matcher(str);
List<String> parts = new ArrayList<String>();
final String trimmedString = (str != null ? str.trim() : str);
Matcher matcher = VERSION_PATTERN.matcher(trimmedString);
List<String> parts = new ArrayList<>();
while (matcher.find()) {
parts.add(matcher.group(1));
}
//default
if (parts.size() == 0) {
if (parts.isEmpty()) {
return "1.0.0";
}
while (parts.size() < 3) {
parts.add("0");
}
while (parts.size() > 3) {
parts.remove(0); // Assume a bad match with extra digits picked up from earlier in the path. Only keep last three parts (x.y.z).
}
return StringUtils.join(parts, ".");
}
protected Properties getProperties(String fileName) {
ResourceFinder finder = new ResourceFinder("");
ResourceFinder finder = new ResourceFinder(StringUtils.EMPTY);
try {
return finder.findProperties(fileName);
} catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error("Unable to read property file [#]", fileName);
LOG.error("Unable to read the property file [{}]", fileName, e);
if (searchForPropertiesFilesInRelativePath()) {
try {
LOG.warn("Relative path search is enabled. Retry attempt to read the property file [{}] from the relative path", fileName);
return findPropertiesFileInRelativePath(fileName);
} catch (IOException ioex) {
LOG.error("Unable to read the property file [{}] using the relative path", fileName, ioex);
}
}
return new Properties();
}
}
/**
* Check ServletContext initialization parameter "struts.osgi.searchForPropertiesFilesInRelativePath".
*
* @return true if "struts.osgi.searchForPropertiesFilesInRelativePath" is "true" in ServletContext initialization parameters, false otherwise.
*/
protected boolean searchForPropertiesFilesInRelativePath() {
if (this.servletContext != null) {
final String searchForPropertiesFilesInRelativePath = getServletContextParam("struts.osgi.searchForPropertiesFilesInRelativePath", "false");
return Boolean.parseBoolean(searchForPropertiesFilesInRelativePath);
}
return false;
}
/**
* Attempt to read a properties file from the relative path of the current classloader.
* Intended as an alternate configuration fallback for special scenarios where the default lookup
* is not functional (such as for unit tests).
*
* @param fileName the filename (relative path) of the properties file.
* @return a Properties bundle loaded from the provided fileName.
* @throws IOException if the properties file does not exist or cannot be loaded.
*/
protected Properties findPropertiesFileInRelativePath(String fileName) throws IOException {
if (fileName == null || fileName.toLowerCase().endsWith(".class") || fileName.toLowerCase().endsWith(".jar")) {
throw new IllegalArgumentException("Provided file name cannot be null, nor should it be a class or jar file");
}
final ClassLoaderInterface classLoaderInterface = new ClassLoaderInterfaceDelegate(Thread.currentThread().getContextClassLoader());
final URL fileUrl = classLoaderInterface.getResource(fileName);
try (InputStream reader = new BufferedInputStream(fileUrl.openStream())) {
Properties properties = new Properties();
properties.load(reader);
return properties;
}
}
}
@@ -21,6 +21,7 @@ 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;
@@ -30,46 +31,119 @@ 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 comntainer starts</p>
* <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() {
//load properties from felix embedded file
Properties configProps = getProperties("default.properties");
// 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
Properties strutsConfigProps = getProperties("struts-osgi.properties");
addExportedPackages(strutsConfigProps, configProps);
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
addAutoStartBundles(configProps);
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 = System.getProperty("java.io.tmpdir") + ".felix-cache";
configProps.setProperty(Constants.FRAMEWORK_STORAGE, storageDir);
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");
@@ -78,6 +152,12 @@ public class FelixOsgiHost extends BaseOsgiHost {
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"));
@@ -86,14 +166,71 @@ public class FelixOsgiHost extends BaseOsgiHost {
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 (Exception ex) {
throw new ConfigurationException("Couldn't start Apache Felix", ex);
catch (Throwable t) {
throw new ConfigurationException("FelixOSGiHost couldn't start Apache Felix", t);
}
addSpringOSGiSupport();
@@ -110,7 +247,10 @@ public class FelixOsgiHost extends BaseOsgiHost {
@Override
public Map<String, Bundle> getBundles() {
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
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);
}
@@ -120,7 +260,10 @@ public class FelixOsgiHost extends BaseOsgiHost {
@Override
public Map<String, Bundle> getActiveBundles() {
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
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);
@@ -131,13 +274,28 @@ public class FelixOsgiHost extends BaseOsgiHost {
@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 {
felix.stop();
LOG.trace("Apache Felix has stopped");
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
@@ -148,6 +306,9 @@ public class FelixOsgiHost extends BaseOsgiHost {
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()) {
@@ -158,4 +319,290 @@ public class FelixOsgiHost extends BaseOsgiHost {
"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;
}
}
@@ -80,9 +80,11 @@ public class GlassfishOSGiHost extends BaseOsgiHost implements OsgiHost {
}
private void installBundles() throws Exception {
ArrayList<Bundle> installed = new ArrayList<Bundle>();
ArrayList<Bundle> installed = new ArrayList<>();
for (URL url : findBundles()) {
LOG.debug("Installing bundle [" + url + "]");
if (LOG.isDebugEnabled()) { // Avoid repeated cost of URL string transform, except when debug level logging is enabled.
LOG.debug("Installing bundle [{}]", url);
}
Bundle bundle = bctx.installBundle(url.toExternalForm());
installed.add(bundle);
}
@@ -90,15 +92,15 @@ public class GlassfishOSGiHost extends BaseOsgiHost implements OsgiHost {
try {
bundle.start();
} catch (BundleException e) {
e.printStackTrace();
LOG.error("Failed to start " + bundle, e);
// Use logging to print the stacktrace (no direct stacktrace print to stdout/stderr).
LOG.error("Failed to start [{}]", bundle, e);
}
}
}
private List<URL> findBundles() throws Exception {
ArrayList<URL> list = new ArrayList<URL>();
ArrayList<URL> list = new ArrayList<>();
for (Object o : this.servletContext.getResourcePaths(BUNDLES_DIR)) {
String name = (String) o;
if (name.endsWith(".jar")) {
@@ -132,12 +134,9 @@ public class GlassfishOSGiHost extends BaseOsgiHost implements OsgiHost {
"BUNDLE_CONTEXT_ATTRIBUTE").get(null);
servletContext.setAttribute(key, bctx);
} catch (ClassNotFoundException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Spring OSGi support is not enabled");
}
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",
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);
}
}
@@ -148,7 +147,7 @@ public class GlassfishOSGiHost extends BaseOsgiHost implements OsgiHost {
*/
@Override
public Map<String, Bundle> getBundles() {
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
Map<String, Bundle> bundles = new HashMap<>();
for (Bundle bundle : bctx.getBundles()) {
bundles.put(bundle.getSymbolicName(), bundle);
}
@@ -156,8 +155,9 @@ public class GlassfishOSGiHost extends BaseOsgiHost implements OsgiHost {
return Collections.unmodifiableMap(bundles);
}
@Override
public Map<String, Bundle> getActiveBundles() {
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
Map<String, Bundle> bundles = new HashMap<>();
for (Bundle bundle : bctx.getBundles()) {
if (bundle.getState() == Bundle.ACTIVE)
bundles.put(bundle.getSymbolicName(), bundle);
@@ -166,10 +166,12 @@ public class GlassfishOSGiHost extends BaseOsgiHost implements OsgiHost {
return Collections.unmodifiableMap(bundles);
}
@Override
public BundleContext getBundleContext() {
return bctx;
}
@Override
public void destroy() throws Exception {
}
@@ -43,13 +43,17 @@ public class OsgiInterceptor extends AbstractInterceptor {
private BundleContext bundleContext;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
LOG.trace("OSGi Interceptor - intercept() called - ActionInvocation: [{}]", invocation);
if (bundleContext != null) {
Object action = invocation.getAction();
//inject BundleContext
if (action instanceof BundleContextAware)
if (action instanceof BundleContextAware) {
((BundleContextAware)action).setBundleContext(bundleContext);
}
//inject service implementations
if (action instanceof ServiceAware) {
@@ -69,12 +73,14 @@ public class OsgiInterceptor extends AbstractInterceptor {
for (ServiceReference ref : refs) {
Object service = bundleContext.getService(ref);
//wow, that's a lot of nested ifs
if (service != null)
if (service != null) {
services.add(service);
}
}
if (!services.isEmpty())
((ServiceAware)action).setServices(services);
if (!services.isEmpty()) {
((ServiceAware) action).setServices(services);
}
}
}
}
@@ -82,8 +88,8 @@ public class OsgiInterceptor extends AbstractInterceptor {
}
}
}
} else if (LOG.isWarnEnabled()){
LOG.warn("The OSGi interceptor was not able to find the BundleContext in the ServletContext");
} else {
LOG.warn("The OSGi interceptor was not able to find the BundleContext in the ServletContext");
}
return invocation.invoke();
@@ -91,6 +97,8 @@ public class OsgiInterceptor extends AbstractInterceptor {
@Inject
public void setServletContext(ServletContext servletContext) {
LOG.trace("OSGi Interceptor - setServletContext() called - ServletContext: [{}] ", servletContext);
this.bundleContext = (BundleContext) servletContext.getAttribute(OsgiHost.OSGI_BUNDLE_CONTEXT);
}
@@ -33,6 +33,7 @@ public class StaticContentBundleResourceLoader extends DefaultStaticContentLoade
private BundleAccessor bundleAccessor;
@Override
protected URL findResource(String path) throws IOException {
return bundleAccessor.loadResourceFromAllBundles(path);
}
@@ -29,6 +29,7 @@ import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
*/
public class VelocityBundleResourceLoader extends ClasspathResourceLoader {
@Override
public synchronized InputStream getResourceStream(String name)
throws ResourceNotFoundException {
if ((name == null) || (name.length() == 0)) {
@@ -17,7 +17,7 @@
# under the License.
#
scanning.package.includes = com.opensymphony.xwork2, \
org.apache.commons.lang.xwork, \
org.apache.commons.lang3, \
org.apache.struts2, \
ognl, \
freemarker, \
@@ -22,7 +22,7 @@
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts order="10">
<constant name="struts.objectFactory" value="osgi" />
<constant name="struts.objectFactory.delegate" value="struts" />
@@ -33,7 +33,7 @@
<bean type="org.apache.struts2.osgi.BundleAccessor" class="org.apache.struts2.osgi.DefaultBundleAccessor" />
<bean type="org.apache.struts2.osgi.PackageLoader" class="org.apache.struts2.osgi.BundlePackageLoader" />
<bean name="osgi" type="com.opensymphony.xwork2.ObjectFactory" class="org.apache.struts2.osgi.DelegatingObjectFactory" />
<bean name="springOsgi" type="com.opensymphony.xwork2.ObjectFactory" class="org.apache.struts2.osgi.SpringOsgiObjectFactory" />
<bean name="osgi" type="com.opensymphony.xwork2.config.PackageProvider" class="org.apache.struts2.osgi.OsgiConfigurationProvider" />
@@ -0,0 +1,158 @@
/*
* Copyright 2020 Apache Software Foundation.
*
* Licensed 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;
import org.junit.Assert;
import org.junit.Test;
/**
* Basic tests for OsgiUtil
*/
public class OsgiUtilTest {
/*
* An independent unit test for OsgiUtil.translateBundleURLToJarURL() would be nice, but appears
* complicated to do without either creating and installing a custom URL handler or using
* one of the Mock frameworks that can mock final classes like URL.
*/
@Test
public void testGetBean() {
MockBeanFactory mockBeanFactory = new MockBeanFactory();
Object mockBean = OsgiUtil.getBean(mockBeanFactory, "1000");
Assert.assertNotNull("OsgiUtil getBean() on mock bean factory returned null ?", mockBean);
Assert.assertTrue("Mock bean from mock bean factory has mismatched id ?", mockBeanFactory.beanMatchesId(mockBean, "1000"));
mockBean = OsgiUtil.getBean(new Object(), "1000"); // Logs an error (coverage only).
Assert.assertNull("OsgiUtil getBean() on normal object found a getBean() method ?", mockBean);
}
@Test
public void testContainsBean() {
MockBeanFactory mockBeanFactory = new MockBeanFactory();
Assert.assertTrue("OsgiUtil containsBean() on mock bean factory with numeric id is false ?", OsgiUtil.containsBean(mockBeanFactory, "1000"));
Assert.assertFalse("OsgiUtil containsBean() on mock bean factory with nonnumeric id is true ?", OsgiUtil.containsBean(mockBeanFactory, "NotANumber"));
Assert.assertFalse("OsgiUtil containsBean() on mock bean factory with null id is true ?", OsgiUtil.containsBean(mockBeanFactory, null));
}
@Test
public void testGenerateJavaVersionForSystemPackages() {
// Some test patterns copied from OgnlRuntimeTest.testMajorJavaVersionParse() in OGNL 3.2.x.
// Pre-JDK 9 version strings.
Assert.assertEquals("JDK 5 generate java version failed ?", "1.5", OsgiUtil.generateJavaVersionForSystemPackages("1.5"));
Assert.assertEquals("JDK 5 generate java version failed ?", "1.5", OsgiUtil.generateJavaVersionForSystemPackages("1.5.0"));
Assert.assertEquals("JDK 5 generate java version failed ?", "1.5", OsgiUtil.generateJavaVersionForSystemPackages("1.5.0_21-b11"));
Assert.assertEquals("JDK 6 generate java version failed ?", "1.6", OsgiUtil.generateJavaVersionForSystemPackages("1.6"));
Assert.assertEquals("JDK 6 generate java version failed ?", "1.6", OsgiUtil.generateJavaVersionForSystemPackages("1.6.0"));
Assert.assertEquals("JDK 6 generate java version failed ?", "1.6", OsgiUtil.generateJavaVersionForSystemPackages("1.6.0_43-b19"));
Assert.assertEquals("JDK 7 generate java version failed ?", "1.7", OsgiUtil.generateJavaVersionForSystemPackages("1.7"));
Assert.assertEquals("JDK 7 generate java version failed ?", "1.7", OsgiUtil.generateJavaVersionForSystemPackages("1.7.0"));
Assert.assertEquals("JDK 7 generate java version failed ?", "1.7", OsgiUtil.generateJavaVersionForSystemPackages("1.7.0_79-b15"));
Assert.assertEquals("JDK 8 generate java version failed ?", "1.8", OsgiUtil.generateJavaVersionForSystemPackages("1.8"));
Assert.assertEquals("JDK 8 generate java version failed ?", "1.8", OsgiUtil.generateJavaVersionForSystemPackages("1.8.0"));
Assert.assertEquals("JDK 8 generate java version failed ?", "1.8", OsgiUtil.generateJavaVersionForSystemPackages("1.8.0_201-b20"));
Assert.assertEquals("JDK 8 generate java version failed ?", "1.8", OsgiUtil.generateJavaVersionForSystemPackages("1.8.0-someopenjdkstyle"));
Assert.assertEquals("JDK 8 generate java version failed ?", "1.8", OsgiUtil.generateJavaVersionForSystemPackages("1.8.0_201-someopenjdkstyle"));
// JDK 9 and later version strings.
Assert.assertEquals("JDK 9 generate java version failed ?", "9.0", OsgiUtil.generateJavaVersionForSystemPackages("9"));
Assert.assertEquals("JDK 9 generate java version failed ?", "9.0", OsgiUtil.generateJavaVersionForSystemPackages("9-ea+19"));
Assert.assertEquals("JDK 9 generate java version failed ?", "9.0", OsgiUtil.generateJavaVersionForSystemPackages("9+100"));
Assert.assertEquals("JDK 9 generate java version failed ?", "9.0", OsgiUtil.generateJavaVersionForSystemPackages("9-ea+19"));
Assert.assertEquals("JDK 9 generate java version failed ?", "9.1", OsgiUtil.generateJavaVersionForSystemPackages("9.1.3+15"));
Assert.assertEquals("JDK 9 generate java version failed ?", "9.0", OsgiUtil.generateJavaVersionForSystemPackages("9-someopenjdkstyle"));
Assert.assertEquals("JDK 10 generate java version failed ?", "10.0", OsgiUtil.generateJavaVersionForSystemPackages("10"));
Assert.assertEquals("JDK 10 generate java version failed ?", "10.0", OsgiUtil.generateJavaVersionForSystemPackages("10-ea+11"));
Assert.assertEquals("JDK 10 generate java version failed ?", "10.0", OsgiUtil.generateJavaVersionForSystemPackages("10+10"));
Assert.assertEquals("JDK 10 generate java version failed ?", "10.0", OsgiUtil.generateJavaVersionForSystemPackages("10-ea+11"));
Assert.assertEquals("JDK 10 generate java version failed ?", "10.1", OsgiUtil.generateJavaVersionForSystemPackages("10.1.3+15"));
Assert.assertEquals("JDK 10 generate java version failed ?", "10.0", OsgiUtil.generateJavaVersionForSystemPackages("10-someopenjdkstyle"));
Assert.assertEquals("JDK 11 generate java version failed ?", "11.0", OsgiUtil.generateJavaVersionForSystemPackages("11"));
Assert.assertEquals("JDK 11 generate java version failed ?", "11.0", OsgiUtil.generateJavaVersionForSystemPackages("11-ea+22"));
Assert.assertEquals("JDK 11 generate java version failed ?", "11.0", OsgiUtil.generateJavaVersionForSystemPackages("11+33"));
Assert.assertEquals("JDK 11 generate java version failed ?", "11.0", OsgiUtil.generateJavaVersionForSystemPackages("11-ea+19"));
Assert.assertEquals("JDK 11 generate java version failed ?", "11.1", OsgiUtil.generateJavaVersionForSystemPackages("11.1.3+15"));
Assert.assertEquals("JDK 11 generate java version failed ?", "11.0", OsgiUtil.generateJavaVersionForSystemPackages("11-someopenjdkstyle"));
}
@Test
public void testGenerateJava_SE_SystemPackageVersionString() {
// Some test patterns copied from OgnlRuntimeTest.testMajorJavaVersionParse() in OGNL 3.2.x.
// Pre-JDK 9 version strings.
Assert.assertEquals("JDK 5 generate java version failed ?", "0.0.0.JavaSE_001_005", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.5"));
Assert.assertEquals("JDK 5 generate java version failed ?", "0.0.0.JavaSE_001_005", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.5.0"));
Assert.assertEquals("JDK 5 generate java version failed ?", "0.0.0.JavaSE_001_005", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.5.0_21-b11"));
Assert.assertEquals("JDK 6 generate java version failed ?", "0.0.0.JavaSE_001_006", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.6"));
Assert.assertEquals("JDK 6 generate java version failed ?", "0.0.0.JavaSE_001_006", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.6.0"));
Assert.assertEquals("JDK 6 generate java version failed ?", "0.0.0.JavaSE_001_006", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.6.0_43-b19"));
Assert.assertEquals("JDK 7 generate java version failed ?", "0.0.0.JavaSE_001_007", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.7"));
Assert.assertEquals("JDK 7 generate java version failed ?", "0.0.0.JavaSE_001_007", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.7.0"));
Assert.assertEquals("JDK 7 generate java version failed ?", "0.0.0.JavaSE_001_007", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.7.0_79-b15"));
Assert.assertEquals("JDK 8 generate java version failed ?", "0.0.0.JavaSE_001_008", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.8"));
Assert.assertEquals("JDK 8 generate java version failed ?", "0.0.0.JavaSE_001_008", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.8.0"));
Assert.assertEquals("JDK 8 generate java version failed ?", "0.0.0.JavaSE_001_008", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.8.0_201-b20"));
Assert.assertEquals("JDK 8 generate java version failed ?", "0.0.0.JavaSE_001_008", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.8.0-someopenjdkstyle"));
Assert.assertEquals("JDK 8 generate java version failed ?", "0.0.0.JavaSE_001_008", OsgiUtil.generateJava_SE_SystemPackageVersionString("1.8.0_201-someopenjdkstyle"));
// JDK 9 and later version strings.
Assert.assertEquals("JDK 9 generate java version failed ?", "0.0.0.JavaSE_009", OsgiUtil.generateJava_SE_SystemPackageVersionString("9"));
Assert.assertEquals("JDK 9 generate java version failed ?", "0.0.0.JavaSE_009", OsgiUtil.generateJava_SE_SystemPackageVersionString("9-ea+19"));
Assert.assertEquals("JDK 9 generate java version failed ?", "0.0.0.JavaSE_009", OsgiUtil.generateJava_SE_SystemPackageVersionString("9+100"));
Assert.assertEquals("JDK 9 generate java version failed ?", "0.0.0.JavaSE_009", OsgiUtil.generateJava_SE_SystemPackageVersionString("9-ea+19"));
Assert.assertEquals("JDK 9 generate java version failed ?", "0.0.0.JavaSE_009", OsgiUtil.generateJava_SE_SystemPackageVersionString("9.1.3+15"));
Assert.assertEquals("JDK 9 generate java version failed ?", "0.0.0.JavaSE_009", OsgiUtil.generateJava_SE_SystemPackageVersionString("9-someopenjdkstyle"));
Assert.assertEquals("JDK 10 generate java version failed ?", "0.0.0.JavaSE_010", OsgiUtil.generateJava_SE_SystemPackageVersionString("10"));
Assert.assertEquals("JDK 10 generate java version failed ?", "0.0.0.JavaSE_010", OsgiUtil.generateJava_SE_SystemPackageVersionString("10-ea+11"));
Assert.assertEquals("JDK 10 generate java version failed ?", "0.0.0.JavaSE_010", OsgiUtil.generateJava_SE_SystemPackageVersionString("10+10"));
Assert.assertEquals("JDK 10 generate java version failed ?", "0.0.0.JavaSE_010", OsgiUtil.generateJava_SE_SystemPackageVersionString("10-ea+11"));
Assert.assertEquals("JDK 10 generate java version failed ?", "0.0.0.JavaSE_010", OsgiUtil.generateJava_SE_SystemPackageVersionString("10.1.3+15"));
Assert.assertEquals("JDK 10 generate java version failed ?", "0.0.0.JavaSE_010", OsgiUtil.generateJava_SE_SystemPackageVersionString("10-someopenjdkstyle"));
Assert.assertEquals("JDK 11 generate java version failed ?", "0.0.0.JavaSE_011", OsgiUtil.generateJava_SE_SystemPackageVersionString("11"));
Assert.assertEquals("JDK 11 generate java version failed ?", "0.0.0.JavaSE_011", OsgiUtil.generateJava_SE_SystemPackageVersionString("11-ea+22"));
Assert.assertEquals("JDK 11 generate java version failed ?", "0.0.0.JavaSE_011", OsgiUtil.generateJava_SE_SystemPackageVersionString("11+33"));
Assert.assertEquals("JDK 11 generate java version failed ?", "0.0.0.JavaSE_011", OsgiUtil.generateJava_SE_SystemPackageVersionString("11-ea+19"));
Assert.assertEquals("JDK 11 generate java version failed ?", "0.0.0.JavaSE_011", OsgiUtil.generateJava_SE_SystemPackageVersionString("11.1.3+15"));
Assert.assertEquals("JDK 11 generate java version failed ?", "0.0.0.JavaSE_011", OsgiUtil.generateJava_SE_SystemPackageVersionString("11-someopenjdkstyle"));
}
private class MockBeanFactory {
public Object getBean(String beanId) {
return "MockBean: " + beanId;
}
public boolean containsBean(String beanId) {
boolean result = false;
if (beanId != null) {
try {
Integer.parseInt(beanId);
result = true;
} catch (NumberFormatException nfe) {
result = false;
}
}
return result;
}
public final boolean beanMatchesId(Object mockBean, String beanId) {
return mockBean.equals("MockBean: " + beanId);
}
}
}
@@ -18,12 +18,53 @@
*/
package org.apache.struts2.osgi.host;
import java.util.Map;
import junit.framework.TestCase;
import org.apache.struts2.osgi.host.FelixOsgiHost;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.springframework.mock.web.MockServletContext;
public class FelixOsgiHostTest extends TestCase {
private static final Logger LOG = LogManager.getLogger(FelixOsgiHostTest.class);
private FelixOsgiHost felixHost;
private MockServletContext servletContext;
private FelixOsgiHost felixHost = new FelixOsgiHost();
@Override
protected void setUp() throws Exception {
super.setUp();
// Use non-public API to explicitly set Log4j2 logging levels for the test.
Configurator.setRootLevel(Level.INFO);
Configurator.setLevel("org.apache.felix.framework.Felix.", Level.INFO);
Configurator.setLevel("org.apache.struts2.osgi.host.BaseOsgiHost", Level.INFO);
Configurator.setLevel("org.apache.struts2.osgi.host.FelixOsgiHost", Level.INFO);
// Set up mock ServletContext for Felix OSGi testing.
servletContext = new MockServletContext();
servletContext.setInitParameter("struts.osgi.clearBundleCache", "true"); // Same as default (true).
servletContext.setInitParameter("struts.osgi.felixCacheLocking", "true"); // Same as default (true). If testing a start-after-init, value must be false (otherwise the second start fails).
servletContext.setInitParameter("struts.osgi.runLevel", "0"); // Different from default (3). Setting to run level above 0 causes error complaints in the Felix initialization for the unit test.
servletContext.setInitParameter("struts.osgi.logLevel", "3"); // INFO instead of default (1) for ERROR.
servletContext.setInitParameter("struts.osgi.searchForPropertiesFilesInRelativePath", "true"); // Different from default (false). Must be true for unit tests.
servletContext.setInitParameter("struts.osgi.felixPropertiesPath", "default.properties"); // Same as default.
servletContext.setInitParameter("struts.osgi.strutsOSGiPropertiesPath", "struts-osgi.properties"); // Same as default.
felixHost = new FelixOsgiHost();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
try {
felixHost.destroy();
} catch (Exception ex) {
// Nothing to be done if destruction fails.
}
}
public void testGetVersionFromString() {
assertEquals("2.1.1", felixHost.getVersionFromString("2.1.1-SNAPSHOT"));
@@ -35,4 +76,127 @@ public class FelixOsgiHostTest extends TestCase {
assertEquals("1.0.0", felixHost.getVersionFromString("something"));
}
public void testInitAndDestroy() {
try {
// Calling init will call start as part of the flow. Calling startFelix() afterwards
// will produce errors, unless destroy has been called first
felixHost.init(servletContext);
} catch (Exception ex) {
if (ex instanceof RuntimeException && ex.getMessage().contains("Unable to create cache directory.")) {
LOG.warn("Felix cache directory could not be created (possible environment issue). Skipping rest of the test.");
return; // Prevent Struts build from failing due to inability to create Felix cache directory.
}
fail("Unable to initialize Felix OSGi container. Exception: " + ex );
}
try {
felixHost.destroy();
} catch (Exception ex) {
fail("Unable to destroy Felix OSGi container after init. Exception: " + ex );
}
// Test to confirm a Felix restart is possible (with cache locking true), using the previously initialized values.
try {
felixHost.startFelix();
} catch (Exception ex) {
fail("Unable to start Felix OSGi container after destroy. Exception: " + ex );
}
try {
felixHost.destroy();
} catch (Exception ex) {
fail("Unable to destroy Felix OSGi container after start. Exception: " + ex );
}
}
public void testGetBundles() {
try {
Map<String, Bundle> bundles = felixHost.getBundles();
fail("Expected an IllegalStateException since Felix was never initialized or started");
} catch (IllegalStateException isex) {
// Expected state
} catch (Exception ex) {
fail("Unable to get Felix bundles. Exception: " + ex );
}
try {
felixHost.init(servletContext);
Map<String, Bundle> bundles = felixHost.getBundles();
assertNotNull("Bundles is null ?", bundles);
assertFalse("Bundles is empty ?", bundles.isEmpty());
LOG.info("OSGi Bundles: " + bundles.toString());
} catch (Exception ex) {
if (ex instanceof RuntimeException && ex.getMessage().contains("Unable to create cache directory.")) {
LOG.warn("Felix cache directory could not be created (possible environment issue). Skipping rest of the test.");
return; // Prevent Struts build from failing due to inability to create Felix cache directory.
}
fail("Unable to get Felix bundles. Exception: " + ex );
} finally {
try {
felixHost.destroy();
} catch (Exception ex) {
// Nothing to be done if destruction fails.
}
}
}
public void testGetActiveBundles() {
try {
Map<String, Bundle> bundles = felixHost.getActiveBundles();
fail("Expected an IllegalStateException since Felix was never initialized or started");
} catch (IllegalStateException isex) {
// Expected state
} catch (Exception ex) {
fail("Unable to get Felix active bundles. Exception: " + ex );
}
try {
felixHost.init(servletContext);
Map<String, Bundle> bundles = felixHost.getActiveBundles();
assertNotNull("Bundles is null ?", bundles);
assertFalse("Bundles is empty ?", bundles.isEmpty());
LOG.info("OSGi Active Bundles: " + bundles.toString());
} catch (Exception ex) {
if (ex instanceof RuntimeException && ex.getMessage().contains("Unable to create cache directory.")) {
LOG.warn("Felix cache directory could not be created (possible environment issue). Skipping rest of the test.");
return; // Prevent Struts build from failing due to inability to create Felix cache directory.
}
fail("Unable to get Felix active bundles. Exception: " + ex );
} finally {
try {
felixHost.destroy();
} catch (Exception ex) {
// Nothing to be done if destruction fails.
}
}
}
public void testGetBundleContext() {
try {
BundleContext bundleContext = felixHost.getBundleContext();
fail("Expected an IllegalStateException since Felix was never initialized or started");
} catch (IllegalStateException isex) {
// Expected state
} catch (Exception ex) {
fail("Unable to get Felix bundle context. Exception: " + ex );
}
try {
felixHost.init(servletContext);
BundleContext bundleContext = felixHost.getBundleContext();
assertNotNull("Bundle context is null ?", bundleContext);
} catch (Exception ex) {
if (ex instanceof RuntimeException && ex.getMessage().contains("Unable to create cache directory.")) {
LOG.warn("Felix cache directory could not be created (possible environment issue). Skipping rest of the test.");
return; // Prevent Struts build from failing due to inability to create Felix cache directory.
}
fail("Unable to get Felix bundle context. Exception: " + ex );
} finally {
try {
felixHost.destroy();
} catch (Exception ex) {
// Nothing to be done if destruction fails.
}
}
}
}
@@ -28,6 +28,7 @@ public class SomeAction implements ServiceAware<Object> {
return services;
}
@Override
public void setServices(List<Object> services) {
this.services = services;
}
@@ -0,0 +1,787 @@
#
# 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.
#
#
# Apache Felix Configuration Properties
#
# Notes:
#
# - For the current unit tests, any framework startlevel (org.osgi.framework.startlevel.beginning) higher than 0 will
# result in the Felix startup compaining with errors:
#
# "ERROR: Bundle osgi.core [2] Error starting file:/somepath/osgi.core-7.0.0.jar (org.osgi.framework.BundleException: Unable to resolve osgi.core [2](R 2.0):" \
# " missing requirement [osgi.core [2](R 2.0)] osgi.unresolvable; (&(must.not.resolve=*)(!(must.not.resolve=*))) Unresolved requirements:" \
# " [[osgi.core [2](R 2.0)] osgi.unresolvable; (&(must.not.resolve=*)(!(must.not.resolve=*)))])".
#
# The only match for this error that could be found was at https://bz.apache.org/netbeans/show_bug.cgi?id=195862 ,
# which indicated their resolution was to mark the osgi-core jar dependency scope as "provided". Trying that same change
# in the project POM did not resolve the error for the unit tests (and might not be safe to do for the plugin, anyway).
#
# Since no solution has been found to avoid the error with higher startlevels, the current option is to set the framework
# startlevel to 0. Maybe it is due to some odd classpath dependency, or the fact that we are trying to run Felix from
# within Maven without using the maven-bundle-plugin (since we are not actually creating a bunde with an Export-Package).
# It could also be because we do not have a real bundles directory for Felix in the unit tests, so it defaults to the
# felix-cache directory as its bundle directory.
#
# - If attempting a second start while another instance is active, cache locking (felix.cache.locking) must be set to false.
# If you see:
#
# "ERROR: Error creating bundle cache. java.lang.Exception: Unable to lock bundle cache: java.nio.channels.OverlappingFileLockException"
#
# in the output from one of the unit tests, setting the value to false is a workaround. Before changing the felix.cache.locking level,
# check the logic of the unit test to *confirm* that you intended to start a second concurrent instance of Felix (otherwise
# it is probably due to a bug in the unit test logic).
#
# - If Felix can be initialized by the unit tests, using this configuration, it provides a minimal sanity check that the
# features of the Struts 2 OSGi plugin that modify the properties file still work.
#
# Common Settings: Logging level: Info. Locking: True. StartLevel: 0. Storage Clean: On first initialization.
#
felix.log.level=3
felix.cache.locking=true
org.osgi.framework.startlevel.beginning=0
org.osgi.framework.storage.clean=onFirstInit
#
# OSGi Framework Configuration Properties.
#
# Based on default.properties from within Felix 6.0.3 (org.apache.felix.main-6.0.3.jar)
#
#org.osgi.service.http.port=8080 # Reference only
# New-style generic execution environment capabilities.
# Concatenated from original Felix properties, attempted to extend for Java 9, 10, 11.
org.osgi.framework.system.capabilities= \
osgi.service; objectClass:List<String>=org.osgi.service.resolver.Resolver; uses:=org.osgi.service.resolver, \
osgi.service; objectClass:List<String>=org.osgi.service.startlevel.StartLevel; uses:=org.osgi.service.startlevel, \
osgi.service; objectClass:List<String>=org.osgi.service.packageadmin.PackageAdmin; uses:=org.osgi.service.packageadmin, \
osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1,1.2", \
osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,9,10,11", \
osgi.ee; osgi.ee="JavaSE/compact1"; version:List<Version>="1.8,9,10,11", \
osgi.ee; osgi.ee="JavaSE/compact2"; version:List<Version>="1.8,9,10,11", \
osgi.ee; osgi.ee="JavaSE/compact3"; version:List<Version>="1.8,9,10,11"
# Deprecated old-style execution environment properties.
# Best guess for execution environment from Java 11 and earlier, based on Felix 6.0.3 default.properties.
org.osgi.framework.executionenvironment= \
JavaSE-11,JavaSE-10,JavaSE-9,JavaSE-1.8,JavaSE-1.7,JavaSE-1.6,J2SE-1.5,J2SE-1.4,J2SE-1.3, \
J2SE-1.2,JRE-1.1,JRE-1.0,OSGi/Minimum-1.2,OSGi/Minimum-1.1, \
OSGi/Minimum-1.0
# Framework system packages.
# Listing from Felix 6.0.3 default.properties, and osgi-core-7.0 manifest, modified slightly to be alphabetical.
# Also changed to allow for the jre-specification-version detection in the Struts 2 Struts 2 OSGi plugin.
org.osgi.framework.system.packages=\
org.osgi.dto; version="1.1.0", \
org.osgi.framework; version="1.9.0", \
org.osgi.framework.dto; version="1.8.0"; uses:="org.osgi.dto", \
org.osgi.framework.hooks.bundle; version="1.1.0"; uses:="org.osgi.framework", \
org.osgi.framework.hooks.resolver; version="1.0.0"; uses:="org.osgi.framework.wiring", \
org.osgi.framework.hooks.service; version="1.1.0"; uses:="org.osgi.framework", \
org.osgi.framework.hooks.weaving; version="1.1.0"; uses:="org.osgi.framework.wiring", \
org.osgi.framework.launch; version="1.2.0"; uses:="org.osgi.framework", \
org.osgi.framework.namespace; version="1.1.0"; uses:="org.osgi.resource", \
org.osgi.framework.startlevel; version="1.0.0"; uses:="org.osgi.framework", \
org.osgi.framework.startlevel.dto; version="1.0.0"; uses:="org.osgi.dto", \
org.osgi.framework.wiring; version="1.2.0"; uses:="org.osgi.framework,org.osgi.resource", \
org.osgi.framework.wiring.dto; version="1.3.0"; uses:="org.osgi.dto,org.osgi.resource.dto", \
org.osgi.resource; version="1.0.0", \
org.osgi.resource.dto; version="1.0.0"; uses:="org.osgi.dto", \
org.osgi.service.packageadmin; version="1.2.0"; uses:="org.osgi.framework", \
org.osgi.service.startlevel; version="1.1.0"; uses:="org.osgi.framework", \
org.osgi.service.resolver; version="1.1.0"; uses:="org.osgi.resource", \
org.osgi.service.url; version="1.0.0", \
org.osgi.util.tracker; version="1.5.2"; uses:="org.osgi.framework" \
${jre-${java.specification.version}}
# Note: Sometimes the line "${jre-${java.specification.version}}" is not preceded by a ", \" in the line above (for a
# typical Felix configuration). Make sure to either include ", \" preceding it in the line above, or as the
# first line in jre-x.y in your configuration. Forgetting the ", \", or having duplicates, will result in
# configuration parsing errors for Felix.
# Felix, when running standalone, can perform substitution includes for the jre-x.y system package properties elements
# to simplify inclusions. The Struts 2 OSGi plugin must have literal elements for the system package properties,
# so there may be some redundancy if supporting multiple java versions. This test properties file provides a fairly
# complete sample, but see the Felix default.properties or active headers in a running Felix instance for an up-to-date
# listing.
#
# Note: Felix uses "${felix.detect.java.version}" for its replacement string, while the Struts 2 OSGi plugin
# uses "${detect.java.version}" instead. The end result should be version elements like "0.0.0.JavaSE_001_008" for
# Java 8 or older, and "0.0.0.JavaSE_009" for Java 9 and newer (009 for 9, 010 for 10, 011 for 11, etc.).
# As of Struts 2.5.24, the Struts 2 OSGi plugin has added some support for replacing the Felix-style strings,
# but for clarity your configuration should probably use only one style of replacement string identifiers.
# Structured from Felix 6.0.3 default.properties for jre-1.7.
# Note: May contain minor errors, could not be interactively verified using felix:headers under Java 7.
jre-1.7=, \
java.applet; version="${detect.java.version}", \
java.awt; version="${detect.java.version}", \
java.awt.color; version="${detect.java.version}", \
java.awt.datatransfer; version="${detect.java.version}", \
java.awt.dnd; version="${detect.java.version}", \
java.awt.event; version="${detect.java.version}", \
java.awt.font; version="${detect.java.version}", \
java.awt.geom; version="${detect.java.version}", \
java.awt.im; version="${detect.java.version}", \
java.awt.im.spi; version="${detect.java.version}", \
java.awt.image; version="${detect.java.version}", \
java.awt.image.renderable; version="${detect.java.version}", \
java.awt.print; version="${detect.java.version}", \
java.beans; version="${detect.java.version}", \
java.beans.beancontext; version="${detect.java.version}", \
java.io; version="${detect.java.version}", \
java.lang; version="${detect.java.version}", \
java.lang.annotation; version="${detect.java.version}", \
java.lang.instrument; version="${detect.java.version}", \
java.lang.invoke; version="${detect.java.version}", \
java.lang.management; version="${detect.java.version}", \
java.lang.ref; version="${detect.java.version}", \
java.lang.reflect; version="${detect.java.version}", \
java.math; version="${detect.java.version}", \
java.net; version="${detect.java.version}", \
java.nio; version="${detect.java.version}", \
java.nio.channels; version="${detect.java.version}", \
java.nio.channels.spi; version="${detect.java.version}", \
java.nio.charset; version="${detect.java.version}", \
java.nio.charset.spi; version="${detect.java.version}", \
java.nio.file; version="${detect.java.version}", \
java.nio.file.attribute; version="${detect.java.version}", \
java.nio.file.spi; version="${detect.java.version}", \
java.rmi; version="${detect.java.version}", \
java.rmi.activation; version="${detect.java.version}", \
java.rmi.dgc; version="${detect.java.version}", \
java.rmi.registry; version="${detect.java.version}", \
java.rmi.server; version="${detect.java.version}", \
java.security; version="${detect.java.version}", \
java.security.acl; version="${detect.java.version}", \
java.security.cert; version="${detect.java.version}", \
java.security.interfaces; version="${detect.java.version}", \
java.security.spec; version="${detect.java.version}", \
java.sql; version="${detect.java.version}", \
java.text; version="${detect.java.version}", \
java.text.spi; version="${detect.java.version}", \
java.util; version="${detect.java.version}", \
java.util.concurrent; version="${detect.java.version}", \
java.util.concurrent.atomic; version="${detect.java.version}", \
java.util.concurrent.locks; version="${detect.java.version}", \
java.util.jar; version="${detect.java.version}", \
java.util.logging; version="${detect.java.version}", \
java.util.prefs; version="${detect.java.version}", \
java.util.regex; version="${detect.java.version}", \
java.util.spi; version="${detect.java.version}", \
java.util.zip; version="${detect.java.version}", \
javax.accessibility; uses:="javax.swing.text"; version="${detect.java.version}", \
javax.activation; version="${detect.java.version}", \
javax.activity; version="${detect.java.version}", \
javax.annotation; version="${detect.java.version}", \
javax.annotation.processing; uses:="javax.lang.model,javax.lang.model.element,javax.lang.model.util,javax.tools"; version="${detect.java.version}", \
javax.crypto.interfaces; uses:="javax.crypto,javax.crypto.spec"; version="${detect.java.version}", \
javax.crypto.spec; uses:="javax.crypto"; version="${detect.java.version}", \
javax.imageio; uses:="javax.imageio.event,javax.imageio.metadata,javax.imageio.spi,javax.imageio.stream"; version="${detect.java.version}", \
javax.imageio.event; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.metadata; uses:="javax.imageio,org.w3c.dom"; version="${detect.java.version}", \
javax.imageio.plugins.bmp; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.plugins.jpeg; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.spi; uses:="javax.imageio,javax.imageio.metadata,javax.imageio.stream"; version="${detect.java.version}", \
javax.imageio.stream; uses:="javax.imageio"; version="${detect.java.version}", \
javax.jws; version="${detect.java.version}", \
javax.jws.soap; version="${detect.java.version}", \
javax.lang.model.element; uses:="javax.lang.model,javax.lang.model.type"; version="${detect.java.version}", \
javax.lang.model.type; uses:="javax.lang.model,javax.lang.model.element"; version="${detect.java.version}", \
javax.lang.model.util; uses:="javax.annotation.processing,javax.lang.model,javax.lang.model.element,javax.lang.model.type"; version="${detect.java.version}", \
javax.management; uses:="javax.management.loading,javax.management.openmbean"; version="${detect.java.version}", \
javax.management.loading; uses:="javax.management"; version="${detect.java.version}", \
javax.management.modelmbean; uses:="javax.management,javax.management.loading"; version="${detect.java.version}", \
javax.management.monitor; uses:="javax.management"; version="${detect.java.version}", \
javax.management.openmbean; uses:="javax.management"; version="${detect.java.version}", \
javax.management.relation; uses:="javax.management"; version="${detect.java.version}", \
javax.management.remote; uses:="javax.management,javax.security.auth"; version="${detect.java.version}", \
javax.management.remote.rmi; uses:="javax.management,javax.management.loading,javax.management.remote,javax.naming,javax.rmi,javax.rmi.CORBA,javax.rmi.ssl,javax.security.auth,org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable"; version="${detect.java.version}", \
javax.management.timer; uses:="javax.management"; version="${detect.java.version}", \
javax.naming; uses:="javax.naming.spi"; version="${detect.java.version}", \
javax.naming.directory; uses:="javax.naming"; version="${detect.java.version}", \
javax.naming.event; uses:="javax.naming,javax.naming.directory"; version="${detect.java.version}", \
javax.naming.ldap; uses:="javax.naming,javax.naming.directory,javax.naming.event,javax.net.ssl"; version="${detect.java.version}", \
javax.naming.spi; uses:="javax.naming,javax.naming.directory"; version="${detect.java.version}", \
javax.net; version="${detect.java.version}", \
javax.net.ssl; uses:="javax.net,javax.security.auth.x500,javax.security.cert"; version="${detect.java.version}", \
javax.print; uses:="javax.print.attribute,javax.print.attribute.standard,javax.print.event"; version="${detect.java.version}", \
javax.print.attribute; version="${detect.java.version}", \
javax.print.attribute.standard; uses:="javax.print.attribute"; version="${detect.java.version}", \
javax.print.event; uses:="javax.print,javax.print.attribute"; version="${detect.java.version}", \
javax.rmi; uses:="javax.rmi.CORBA,org.omg.CORBA"; version="${detect.java.version}", \
javax.rmi.CORBA; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable,org.omg.SendingContext"; version="${detect.java.version}", \
javax.rmi.ssl; uses:="javax.net,javax.net.ssl"; version="${detect.java.version}", \
javax.script; version="${detect.java.version}", \
javax.security.auth; version="${detect.java.version}", \
javax.security.auth.callback; version="${detect.java.version}", \
javax.security.auth.kerberos; uses:="javax.crypto,javax.security.auth"; version="${detect.java.version}", \
javax.security.auth.login; uses:="javax.security.auth,javax.security.auth.callback"; version="${detect.java.version}", \
javax.security.auth.spi; uses:="javax.security.auth,javax.security.auth.callback,javax.security.auth.login"; version="${detect.java.version}", \
javax.security.auth.x500; uses:="javax.security.auth"; version="${detect.java.version}", \
javax.security.cert; version="${detect.java.version}", \
javax.security.sasl; uses:="javax.security.auth.callback"; version="${detect.java.version}", \
javax.sound.midi; uses:="javax.sound.midi.spi"; version="${detect.java.version}", \
javax.sound.midi.spi; uses:="javax.sound.midi"; version="${detect.java.version}", \
javax.sound.sampled; uses:="javax.sound.sampled.spi"; version="${detect.java.version}", \
javax.sound.sampled.spi; uses:="javax.sound.sampled"; version="${detect.java.version}", \
javax.sql; uses:="javax.transaction.xa"; version="${detect.java.version}", \
javax.sql.rowset; uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi"; version="${detect.java.version}", \
javax.sql.rowset.serial; uses:="javax.sql.rowset"; version="${detect.java.version}", \
javax.sql.rowset.spi; uses:="javax.naming,javax.sql,javax.sql.rowset"; version="${detect.java.version}", \
javax.swing; uses:="javax.accessibility,javax.print,javax.print.attribute,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.plaf.metal,javax.swing.table,javax.swing.text,javax.swing.text.html,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.border; uses:="javax.swing"; version="${detect.java.version}", \
javax.swing.colorchooser; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.text"; version="${detect.java.version}", \
javax.swing.event; uses:="javax.swing,javax.swing.table,javax.swing.text,javax.swing.tree,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.filechooser; uses:="javax.swing"; version="${detect.java.version}", \
javax.swing.plaf; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.filechooser,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.basic; uses:="javax.accessibility,javax.sound.sampled,javax.swing,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.synth,javax.swing.table,javax.swing.text,javax.swing.text.html,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.nimbus; uses:="javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.synth"; version="${detect.java.version}", \
javax.swing.plaf.metal; uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.multi; uses:="javax.accessibility,javax.swing,javax.swing.filechooser,javax.swing.plaf,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.synth; uses:="javax.swing,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.table,javax.swing.text,javax.swing.tree,javax.xml.parsers,org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.swing.table; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf"; version="${detect.java.version}", \
javax.swing.text; uses:="javax.accessibility,javax.print,javax.print.attribute,javax.swing,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.tree,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.text.html; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf,javax.swing.text,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.text.html.parser; uses:="javax.swing.text,javax.swing.text.html"; version="${detect.java.version}", \
javax.swing.text.rtf; uses:="javax.swing.text"; version="${detect.java.version}", \
javax.swing.tree; uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic"; version="${detect.java.version}", \
javax.swing.undo; uses:="javax.swing,javax.swing.event"; version="${detect.java.version}", \
javax.tools; uses:="javax.annotation.processing,javax.lang.model,javax.lang.model.element"; version="${detect.java.version}", \
javax.transaction; version="${detect.java.version}", \
javax.transaction.xa; version="${detect.java.version}", \
javax.xml; version="${detect.java.version}", \
javax.xml.bind; uses:="javax.xml.bind.annotation,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.datatype,javax.xml.namespace,javax.xml.stream,javax.xml.transform,javax.xml.transform.stream,javax.xml.validation,org.w3c.dom,org.xml.sax"; version="${detect.java.version}", \
javax.xml.bind.annotation; uses:="javax.xml.bind,javax.xml.parsers,javax.xml.transform,javax.xml.transform.dom,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.bind.annotation.adapters; uses:="javax.xml.bind"; version="${detect.java.version}", \
javax.xml.bind.attachment; uses:="javax.activation"; version="${detect.java.version}", \
javax.xml.bind.helpers; uses:="javax.xml.bind,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.parsers,javax.xml.stream,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.sax,javax.xml.transform.stream,javax.xml.validation,org.w3c.dom,org.xml.sax"; version="${detect.java.version}", \
javax.xml.bind.util; uses:="javax.xml.bind,javax.xml.transform.sax,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.xml.crypto; uses:="javax.xml.crypto.dsig.keyinfo"; version="${detect.java.version}", \
javax.xml.crypto.dom; uses:="javax.xml.crypto,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.crypto.dsig; uses:="javax.xml.crypto,javax.xml.crypto.dsig.keyinfo,javax.xml.crypto.dsig.spec"; version="${detect.java.version}", \
javax.xml.crypto.dsig.dom; uses:="javax.xml.crypto,javax.xml.crypto.dom,javax.xml.crypto.dsig,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.crypto.dsig.keyinfo; uses:="javax.xml.crypto"; version="${detect.java.version}", \
javax.xml.crypto.dsig.spec; uses:="javax.xml.crypto"; version="${detect.java.version}", \
javax.xml.datatype; uses:="javax.xml.namespace"; version="${detect.java.version}", \
javax.xml.namespace; version="${detect.java.version}", \
javax.xml.parsers; uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.xml.soap; uses:="javax.activation,javax.xml.namespace,javax.xml.transform,javax.xml.transform.dom,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.stream; uses:="javax.xml.namespace,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform"; version="${detect.java.version}", \
javax.xml.stream.events; uses:="javax.xml.namespace,javax.xml.stream"; version="${detect.java.version}", \
javax.xml.stream.util; uses:="javax.xml.namespace,javax.xml.stream,javax.xml.stream.events"; version="${detect.java.version}", \
javax.xml.transform; version="${detect.java.version}", \
javax.xml.transform.dom; uses:="javax.xml.transform,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.transform.sax; uses:="javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.xml.sax.ext"; version="${detect.java.version}", \
javax.xml.transform.stax; uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.transform"; version="${detect.java.version}", \
javax.xml.transform.stream; uses:="javax.xml.transform"; version="${detect.java.version}", \
javax.xml.validation; uses:="javax.xml.transform,javax.xml.transform.stream,org.w3c.dom,org.w3c.dom.ls,org.xml.sax"; version="${detect.java.version}", \
javax.xml.ws; uses:="javax.xml.bind,javax.xml.bind.annotation,javax.xml.namespace,javax.xml.transform,javax.xml.transform.stream,javax.xml.ws.handler,javax.xml.ws.spi,javax.xml.ws.spi.http,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.ws.handler; uses:="javax.xml.namespace,javax.xml.ws"; version="${detect.java.version}", \
javax.xml.ws.handler.soap; uses:="javax.xml.bind,javax.xml.namespace,javax.xml.soap,javax.xml.ws.handler"; version="${detect.java.version}", \
javax.xml.ws.http; uses:="javax.xml.ws"; version="${detect.java.version}", \
javax.xml.ws.soap; uses:="javax.xml.soap,javax.xml.ws,javax.xml.ws.spi"; version="${detect.java.version}", \
javax.xml.ws.spi; uses:="javax.xml.bind,javax.xml.namespace,javax.xml.transform,javax.xml.ws,javax.xml.ws.handler,javax.xml.ws.wsaddressing,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.ws.spi.http; version="${detect.java.version}", \
javax.xml.ws.wsaddressing; uses:="javax.xml.bind,javax.xml.bind.annotation,javax.xml.namespace,javax.xml.transform,javax.xml.ws,javax.xml.ws.spi,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.xpath; uses:="javax.xml.namespace,org.xml.sax"; version="${detect.java.version}", \
org.ietf.jgss; version="${detect.java.version}", \
org.omg.CORBA; uses:="org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA.TypeCodePackage,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable"; version="${detect.java.version}", \
org.omg.CORBA.DynAnyPackage; uses:="org.omg.CORBA"; version="${detect.java.version}", \
org.omg.CORBA.ORBPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CORBA.TypeCodePackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CORBA.portable; uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable"; version="${detect.java.version}", \
org.omg.CORBA_2_3; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CORBA_2_3.portable; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CosNaming; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming.NamingContextExtPackage,org.omg.CosNaming.NamingContextPackage,org.omg.PortableServer"; version="${detect.java.version}", \
org.omg.CosNaming.NamingContextExtPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CosNaming.NamingContextPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming"; version="${detect.java.version}", \
org.omg.Dynamic; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.DynamicAny; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage"; version="${detect.java.version}", \
org.omg.DynamicAny.DynAnyFactoryPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.DynamicAny.DynAnyPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.IOP; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage"; version="${detect.java.version}", \
org.omg.IOP.CodecFactoryPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.IOP.CodecPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.Messaging; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableInterceptor; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable,org.omg.Dynamic,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage"; version="${detect.java.version}", \
org.omg.PortableInterceptor.ORBInitInfoPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.ServantLocatorPackage,org.omg.PortableServer.portable"; version="${detect.java.version}", \
org.omg.PortableServer.CurrentPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.POAManagerPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.POAPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.ServantLocatorPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.portable; uses:="org.omg.CORBA,org.omg.PortableServer"; version="${detect.java.version}", \
org.omg.SendingContext; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.stub.java.rmi; uses:="javax.rmi.CORBA"; version="${detect.java.version}", \
org.w3c.dom; version="${detect.java.version}", \
org.w3c.dom.bootstrap; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.ls; uses:="org.w3c.dom,org.w3c.dom.events"; version="${detect.java.version}", \
org.w3c.dom.events; uses:="org.w3c.dom,org.w3c.dom.views"; version="${detect.java.version}", \
org.w3c.dom.views; version="${detect.java.version}", \
org.w3c.dom.traversal; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.ranges; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.css; uses:="org.w3c.dom,org.w3c.dom.stylesheets,org.w3c.dom.views"; version="${detect.java.version}", \
org.w3c.dom.html; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.stylesheets; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.xpath; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.xml.sax; version="${detect.java.version}", \
org.xml.sax.ext; uses:="org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
org.xml.sax.helpers; uses:="org.xml.sax"; version="${detect.java.version}"
# Structured from Felix 6.0.3 default.properties for jre-1.8.
# Modified entries based on manual Felix 6.0.3 "felix:headers" operation under Java 8.
jre-1.8=, \
java.applet; version="${detect.java.version}", \
java.awt; version="${detect.java.version}", \
java.awt.color; version="${detect.java.version}", \
java.awt.datatransfer; version="${detect.java.version}", \
java.awt.dnd; version="${detect.java.version}", \
java.awt.event; version="${detect.java.version}", \
java.awt.font; version="${detect.java.version}", \
java.awt.geom; version="${detect.java.version}", \
java.awt.im; version="${detect.java.version}", \
java.awt.im.spi; version="${detect.java.version}", \
java.awt.image; version="${detect.java.version}", \
java.awt.image.renderable; version="${detect.java.version}", \
java.awt.print; version="${detect.java.version}", \
java.beans; version="${detect.java.version}", \
java.beans.beancontext; version="${detect.java.version}", \
java.io; version="${detect.java.version}", \
java.lang; version="${detect.java.version}", \
java.lang.annotation; version="${detect.java.version}", \
java.lang.instrument; version="${detect.java.version}", \
java.lang.invoke; version="${detect.java.version}", \
java.lang.management; version="${detect.java.version}", \
java.lang.ref; version="${detect.java.version}", \
java.lang.reflect; version="${detect.java.version}", \
java.math; version="${detect.java.version}", \
java.net; version="${detect.java.version}", \
java.nio; version="${detect.java.version}", \
java.nio.channels; version="${detect.java.version}", \
java.nio.channels.spi; version="${detect.java.version}", \
java.nio.charset; version="${detect.java.version}", \
java.nio.charset.spi; version="${detect.java.version}", \
java.nio.file; version="${detect.java.version}", \
java.nio.file.attribute; version="${detect.java.version}", \
java.nio.file.spi; version="${detect.java.version}", \
java.rmi; version="${detect.java.version}", \
java.rmi.activation; version="${detect.java.version}", \
java.rmi.dgc; version="${detect.java.version}", \
java.rmi.registry; version="${detect.java.version}", \
java.rmi.server; version="${detect.java.version}", \
java.security; version="${detect.java.version}", \
java.security.acl; version="${detect.java.version}", \
java.security.cert; version="${detect.java.version}", \
java.security.interfaces; version="${detect.java.version}", \
java.security.spec; version="${detect.java.version}", \
java.sql; version="${detect.java.version}", \
java.text; version="${detect.java.version}", \
java.text.spi; version="${detect.java.version}", \
java.time; version="${detect.java.version}", \
java.time.chrono; version="${detect.java.version}", \
java.time.format; version="${detect.java.version}", \
java.time.temporal; version="${detect.java.version}", \
java.time.zone; version="${detect.java.version}", \
java.util; version="${detect.java.version}", \
java.util.concurrent; version="${detect.java.version}", \
java.util.concurrent.atomic; version="${detect.java.version}", \
java.util.concurrent.locks; version="${detect.java.version}", \
java.util.function; version="${detect.java.version}", \
java.util.jar; version="${detect.java.version}", \
java.util.logging; version="${detect.java.version}", \
java.util.prefs; version="${detect.java.version}", \
java.util.regex; version="${detect.java.version}", \
java.util.spi; version="${detect.java.version}", \
java.util.stream; version="${detect.java.version}", \
java.util.zip; version="${detect.java.version}", \
javax.accessibility; uses:="javax.swing.text"; version="${detect.java.version}", \
javax.activation; version="${detect.java.version}", \
javax.activity; version="${detect.java.version}", \
javax.annotation; version="${detect.java.version}", \
javax.annotation.processing; uses:="javax.lang.model,javax.lang.model.element,javax.lang.model.util,javax.tools"; version="${detect.java.version}", \
javax.crypto; uses:="javax.crypto.spec,javax.security.auth"; version="${detect.java.version}", \
javax.crypto.interfaces; uses:="javax.crypto,javax.crypto.spec"; version="${detect.java.version}", \
javax.crypto.spec; uses:="javax.crypto"; version="${detect.java.version}", \
javax.imageio; uses:="javax.imageio.event,javax.imageio.metadata,javax.imageio.spi,javax.imageio.stream"; version="${detect.java.version}", \
javax.imageio.event; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.metadata; uses:="javax.imageio,org.w3c.dom"; version="${detect.java.version}", \
javax.imageio.plugins.bmp; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.plugins.jpeg; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.spi; uses:="javax.imageio,javax.imageio.metadata,javax.imageio.stream"; version="${detect.java.version}", \
javax.imageio.stream; uses:="javax.imageio"; version="${detect.java.version}", \
javax.jws; version="${detect.java.version}", \
javax.jws.soap; version="${detect.java.version}", \
javax.lang.model; uses:="javax.lang.model.element"; version="${detect.java.version}", \
javax.lang.model.element; uses:="javax.lang.model,javax.lang.model.type"; version="${detect.java.version}", \
javax.lang.model.type; uses:="javax.lang.model,javax.lang.model.element"; version="${detect.java.version}", \
javax.lang.model.util; uses:="javax.annotation.processing,javax.lang.model,javax.lang.model.element,javax.lang.model.type"; version="${detect.java.version}", \
javax.management; uses:="javax.management.loading,javax.management.openmbean"; version="${detect.java.version}", \
javax.management.loading; uses:="javax.management"; version="${detect.java.version}", \
javax.management.modelmbean; uses:="javax.management,javax.management.loading"; version="${detect.java.version}", \
javax.management.monitor; uses:="javax.management"; version="${detect.java.version}", \
javax.management.openmbean; uses:="javax.management"; version="${detect.java.version}", \
javax.management.relation; uses:="javax.management"; version="${detect.java.version}", \
javax.management.remote; uses:="javax.management,javax.security.auth"; version="${detect.java.version}", \
javax.management.remote.rmi; uses:="javax.management,javax.management.loading,javax.management.remote,javax.naming,javax.rmi,javax.rmi.CORBA,javax.rmi.ssl,javax.security.auth,org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable"; version="${detect.java.version}", \
javax.management.timer; uses:="javax.management"; version="${detect.java.version}", \
javax.naming; uses:="javax.naming.spi"; version="${detect.java.version}", \
javax.naming.directory; uses:="javax.naming"; version="${detect.java.version}", \
javax.naming.event; uses:="javax.naming,javax.naming.directory"; version="${detect.java.version}", \
javax.naming.ldap; uses:="javax.naming,javax.naming.directory,javax.naming.event,javax.net.ssl"; version="${detect.java.version}", \
javax.naming.spi; uses:="javax.naming,javax.naming.directory"; version="${detect.java.version}", \
javax.net; version="${detect.java.version}", \
javax.net.ssl; uses:="javax.net,javax.security.auth.x500,javax.security.cert"; version="${detect.java.version}", \
javax.print; uses:="javax.print.attribute,javax.print.attribute.standard,javax.print.event"; version="${detect.java.version}", \
javax.print.attribute; version="${detect.java.version}", \
javax.print.attribute.standard; uses:="javax.print.attribute"; version="${detect.java.version}", \
javax.print.event; uses:="javax.print,javax.print.attribute"; version="${detect.java.version}", \
javax.rmi; uses:="javax.rmi.CORBA,org.omg.CORBA"; version="${detect.java.version}", \
javax.rmi.CORBA; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable,org.omg.SendingContext"; version="${detect.java.version}", \
javax.rmi.ssl; uses:="javax.net,javax.net.ssl"; version="${detect.java.version}", \
javax.script; version="${detect.java.version}", \
javax.security.auth; version="${detect.java.version}", \
javax.security.auth.callback; version="${detect.java.version}", \
javax.security.auth.kerberos; uses:="javax.crypto,javax.security.auth"; version="${detect.java.version}", \
javax.security.auth.login; uses:="javax.security.auth,javax.security.auth.callback"; version="${detect.java.version}", \
javax.security.auth.spi; uses:="javax.security.auth,javax.security.auth.callback,javax.security.auth.login"; version="${detect.java.version}", \
javax.security.auth.x500; uses:="javax.security.auth"; version="${detect.java.version}", \
javax.security.cert; version="${detect.java.version}", \
javax.security.sasl; uses:="javax.security.auth.callback"; version="${detect.java.version}", \
javax.sound.midi; uses:="javax.sound.midi.spi"; version="${detect.java.version}", \
javax.sound.midi.spi; uses:="javax.sound.midi"; version="${detect.java.version}", \
javax.sound.sampled; uses:="javax.sound.sampled.spi"; version="${detect.java.version}", \
javax.sound.sampled.spi; uses:="javax.sound.sampled"; version="${detect.java.version}", \
javax.sql; uses:="javax.transaction.xa"; version="${detect.java.version}", \
javax.sql.rowset; uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi"; version="${detect.java.version}", \
javax.sql.rowset.serial; uses:="javax.sql.rowset"; version="${detect.java.version}", \
javax.sql.rowset.spi; uses:="javax.naming,javax.sql,javax.sql.rowset"; version="${detect.java.version}", \
javax.swing; uses:="javax.accessibility,javax.print,javax.print.attribute,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.plaf.metal,javax.swing.table,javax.swing.text,javax.swing.text.html,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.border; uses:="javax.swing"; version="${detect.java.version}", \
javax.swing.colorchooser; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.text"; version="${detect.java.version}", \
javax.swing.event; uses:="javax.swing,javax.swing.table,javax.swing.text,javax.swing.tree,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.filechooser; uses:="javax.swing"; version="${detect.java.version}", \
javax.swing.plaf; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.filechooser,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.basic; uses:="javax.accessibility,javax.sound.sampled,javax.swing,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.synth,javax.swing.table,javax.swing.text,javax.swing.text.html,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.nimbus; uses:="javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.synth"; version="${detect.java.version}", \
javax.swing.plaf.metal; uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.multi; uses:="javax.accessibility,javax.swing,javax.swing.filechooser,javax.swing.plaf,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.synth; uses:="javax.swing,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.table,javax.swing.text,javax.swing.tree,javax.xml.parsers,org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.swing.table; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf"; version="${detect.java.version}", \
javax.swing.text; uses:="javax.accessibility,javax.print,javax.print.attribute,javax.swing,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.tree,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.text.html; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf,javax.swing.text,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.text.html.parser; uses:="javax.swing.text,javax.swing.text.html"; version="${detect.java.version}", \
javax.swing.text.rtf; uses:="javax.swing.text"; version="${detect.java.version}", \
javax.swing.tree; uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic"; version="${detect.java.version}", \
javax.swing.undo; uses:="javax.swing,javax.swing.event"; version="${detect.java.version}", \
javax.tools; uses:="javax.annotation.processing,javax.lang.model,javax.lang.model.element"; version="${detect.java.version}", \
javax.transaction; version="${detect.java.version}", \
javax.transaction.xa; version="${detect.java.version}", \
javax.xml; version="${detect.java.version}", \
javax.xml.bind; uses:="javax.xml.bind.annotation,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.datatype,javax.xml.namespace,javax.xml.stream,javax.xml.transform,javax.xml.transform.stream,javax.xml.validation,org.w3c.dom,org.xml.sax"; version="${detect.java.version}", \
javax.xml.bind.annotation; uses:="javax.xml.bind,javax.xml.parsers,javax.xml.transform,javax.xml.transform.dom,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.bind.annotation.adapters; uses:="javax.xml.bind"; version="${detect.java.version}", \
javax.xml.bind.attachment; uses:="javax.activation"; version="${detect.java.version}", \
javax.xml.bind.helpers; uses:="javax.xml.bind,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.parsers,javax.xml.stream,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.sax,javax.xml.transform.stream,javax.xml.validation,org.w3c.dom,org.xml.sax"; version="${detect.java.version}", \
javax.xml.bind.util; uses:="javax.xml.bind,javax.xml.transform.sax,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.xml.crypto; uses:="javax.xml.crypto.dsig.keyinfo"; version="${detect.java.version}", \
javax.xml.crypto.dom; uses:="javax.xml.crypto,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.crypto.dsig; uses:="javax.xml.crypto,javax.xml.crypto.dsig.keyinfo,javax.xml.crypto.dsig.spec"; version="${detect.java.version}", \
javax.xml.crypto.dsig.dom; uses:="javax.xml.crypto,javax.xml.crypto.dom,javax.xml.crypto.dsig,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.crypto.dsig.keyinfo; uses:="javax.xml.crypto"; version="${detect.java.version}", \
javax.xml.crypto.dsig.spec; uses:="javax.xml.crypto"; version="${detect.java.version}", \
javax.xml.datatype; uses:="javax.xml.namespace"; version="${detect.java.version}", \
javax.xml.namespace; version="${detect.java.version}", \
javax.xml.parsers; uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.xml.soap; uses:="javax.activation,javax.xml.namespace,javax.xml.transform,javax.xml.transform.dom,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.stream; uses:="javax.xml.namespace,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform"; version="${detect.java.version}", \
javax.xml.stream.events; uses:="javax.xml.namespace,javax.xml.stream"; version="${detect.java.version}", \
javax.xml.stream.util; uses:="javax.xml.namespace,javax.xml.stream,javax.xml.stream.events"; version="${detect.java.version}", \
javax.xml.transform; version="${detect.java.version}", \
javax.xml.transform.dom; uses:="javax.xml.transform,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.transform.sax; uses:="javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.xml.sax.ext"; version="${detect.java.version}", \
javax.xml.transform.stax; uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.transform"; version="${detect.java.version}", \
javax.xml.transform.stream; uses:="javax.xml.transform"; version="${detect.java.version}", \
javax.xml.validation; uses:="javax.xml.transform,javax.xml.transform.stream,org.w3c.dom,org.w3c.dom.ls,org.xml.sax"; version="${detect.java.version}", \
javax.xml.ws; uses:="javax.xml.bind,javax.xml.bind.annotation,javax.xml.namespace,javax.xml.transform,javax.xml.transform.stream,javax.xml.ws.handler,javax.xml.ws.spi,javax.xml.ws.spi.http,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.ws.handler; uses:="javax.xml.namespace,javax.xml.ws"; version="${detect.java.version}", \
javax.xml.ws.handler.soap; uses:="javax.xml.bind,javax.xml.namespace,javax.xml.soap,javax.xml.ws.handler"; version="${detect.java.version}", \
javax.xml.ws.http; uses:="javax.xml.ws"; version="${detect.java.version}", \
javax.xml.ws.soap; uses:="javax.xml.soap,javax.xml.ws,javax.xml.ws.spi"; version="${detect.java.version}", \
javax.xml.ws.spi; uses:="javax.xml.bind,javax.xml.namespace,javax.xml.transform,javax.xml.ws,javax.xml.ws.handler,javax.xml.ws.wsaddressing,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.ws.spi.http; version="${detect.java.version}", \
javax.xml.ws.wsaddressing; uses:="javax.xml.bind,javax.xml.bind.annotation,javax.xml.namespace,javax.xml.transform,javax.xml.ws,javax.xml.ws.spi,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.xpath; uses:="javax.xml.namespace,org.xml.sax"; version="${detect.java.version}", \
org.ietf.jgss; version="${detect.java.version}", \
org.omg.CORBA; uses:="org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA.TypeCodePackage,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable"; version="${detect.java.version}", \
org.omg.CORBA.DynAnyPackage; uses:="org.omg.CORBA"; version="${detect.java.version}", \
org.omg.CORBA.ORBPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CORBA.TypeCodePackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CORBA.portable; uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable"; version="${detect.java.version}", \
org.omg.CORBA_2_3; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CORBA_2_3.portable; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CosNaming; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming.NamingContextExtPackage,org.omg.CosNaming.NamingContextPackage,org.omg.PortableServer"; version="${detect.java.version}", \
org.omg.CosNaming.NamingContextExtPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.CosNaming.NamingContextPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming"; version="${detect.java.version}", \
org.omg.Dynamic; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.DynamicAny; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage"; version="${detect.java.version}", \
org.omg.DynamicAny.DynAnyFactoryPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.DynamicAny.DynAnyPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.IOP; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage"; version="${detect.java.version}", \
org.omg.IOP.CodecFactoryPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.IOP.CodecPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.Messaging; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableInterceptor; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable,org.omg.Dynamic,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage"; version="${detect.java.version}", \
org.omg.PortableInterceptor.ORBInitInfoPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer; uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.ServantLocatorPackage,org.omg.PortableServer.portable"; version="${detect.java.version}", \
org.omg.PortableServer.CurrentPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.POAManagerPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.POAPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.ServantLocatorPackage; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.PortableServer.portable; uses:="org.omg.CORBA,org.omg.PortableServer"; version="${detect.java.version}", \
org.omg.SendingContext; uses:="org.omg.CORBA,org.omg.CORBA.portable"; version="${detect.java.version}", \
org.omg.stub.java.rmi; uses:="javax.rmi.CORBA"; version="${detect.java.version}", \
org.w3c.dom; version="${detect.java.version}", \
org.w3c.dom.bootstrap; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.ls; uses:="org.w3c.dom,org.w3c.dom.events"; version="${detect.java.version}", \
org.w3c.dom.events; uses:="org.w3c.dom,org.w3c.dom.views"; version="${detect.java.version}", \
org.w3c.dom.views; version="${detect.java.version}", \
org.w3c.dom.traversal; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.ranges; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.css; uses:="org.w3c.dom,org.w3c.dom.stylesheets,org.w3c.dom.views"; version="${detect.java.version}", \
org.w3c.dom.html; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.stylesheets; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.xpath; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.xml.sax; version="${detect.java.version}", \
org.xml.sax.ext; uses:="org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
org.xml.sax.helpers; uses:="org.xml.sax"; version="${detect.java.version}"
# Almost Empty for Java 9 (fill in if using).
jre-9.0=, \
java.lang; version="${detect.java.version}"
# Almost Empty for Java 10 (fill in if using).
# Not provided
jre-10.0=, \
java.lang; version="${detect.java.version}"
# Copy of jre-1.8 from Felix 6.0.3 default.properties, as no equivalent entry for jre-11.0 exists.
# Modified entries based on manual Felix 6.0.3 "felix:headers" operation under Java 11.
jre-11.0=, \
java.applet; version="${detect.java.version}", \
java.awt; version="${detect.java.version}", \
java.awt.color; version="${detect.java.version}", \
java.awt.datatransfer; version="${detect.java.version}", \
java.awt.desktop; version="${detect.java.version}", \
java.awt.dnd; version="${detect.java.version}", \
java.awt.event; version="${detect.java.version}", \
java.awt.font; version="${detect.java.version}", \
java.awt.geom; version="${detect.java.version}", \
java.awt.im; version="${detect.java.version}", \
java.awt.im.spi; version="${detect.java.version}", \
java.awt.image; version="${detect.java.version}", \
java.awt.image.renderable; version="${detect.java.version}", \
java.awt.print; version="${detect.java.version}", \
java.beans; version="${detect.java.version}", \
java.beans.beancontext; version="${detect.java.version}", \
java.io; version="${detect.java.version}", \
java.lang; version="${detect.java.version}", \
java.lang.annotation; version="${detect.java.version}", \
java.lang.instrument; version="${detect.java.version}", \
java.lang.invoke; version="${detect.java.version}", \
java.lang.management; version="${detect.java.version}", \
java.lang.module; version="${detect.java.version}", \
java.lang.ref; version="${detect.java.version}", \
java.lang.reflect; version="${detect.java.version}", \
java.math; version="${detect.java.version}", \
java.net; version="${detect.java.version}", \
java.net.http; version="${detect.java.version}", \
java.net.spi; version="${detect.java.version}", \
java.nio; version="${detect.java.version}", \
java.nio.channels; version="${detect.java.version}", \
java.nio.channels.spi; version="${detect.java.version}", \
java.nio.charset; version="${detect.java.version}", \
java.nio.charset.spi; version="${detect.java.version}", \
java.nio.file; version="${detect.java.version}", \
java.nio.file.attribute; version="${detect.java.version}", \
java.nio.file.spi; version="${detect.java.version}", \
java.rmi; version="${detect.java.version}", \
java.rmi.activation; version="${detect.java.version}", \
java.rmi.dgc; version="${detect.java.version}", \
java.rmi.registry; version="${detect.java.version}", \
java.rmi.server; version="${detect.java.version}", \
java.security; version="${detect.java.version}", \
java.security.acl; version="${detect.java.version}", \
java.security.cert; version="${detect.java.version}", \
java.security.interfaces; version="${detect.java.version}", \
java.security.spec; version="${detect.java.version}", \
java.sql; version="${detect.java.version}", \
java.text; version="${detect.java.version}", \
java.text.spi; version="${detect.java.version}", \
java.time; version="${detect.java.version}", \
java.time.chrono; version="${detect.java.version}", \
java.time.format; version="${detect.java.version}", \
java.time.temporal; version="${detect.java.version}", \
java.time.zone; version="${detect.java.version}", \
java.util; version="${detect.java.version}", \
java.util.concurrent; version="${detect.java.version}", \
java.util.concurrent.atomic; version="${detect.java.version}", \
java.util.concurrent.locks; version="${detect.java.version}", \
java.util.function; version="${detect.java.version}", \
java.util.jar; version="${detect.java.version}", \
java.util.logging; version="${detect.java.version}", \
java.util.prefs; version="${detect.java.version}", \
java.util.regex; version="${detect.java.version}", \
java.util.spi; version="${detect.java.version}", \
java.util.stream; version="${detect.java.version}", \
java.util.zip; version="${detect.java.version}", \
javax.accessibility; uses:="javax.swing.text"; version="${detect.java.version}", \
javax.annotation.processing; uses:="javax.lang.model,javax.lang.model.element,javax.lang.model.util,javax.tools"; version="${detect.java.version}", \
javax.crypto; uses:="javax.crypto.spec,javax.security.auth"; version="${detect.java.version}", \
javax.crypto.interfaces; uses:="javax.crypto,javax.crypto.spec"; version="${detect.java.version}", \
javax.crypto.spec; uses:="javax.crypto"; version="${detect.java.version}", \
javax.imageio; uses:="javax.imageio.event,javax.imageio.metadata,javax.imageio.spi,javax.imageio.stream"; version="${detect.java.version}", \
javax.imageio.event; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.metadata; uses:="javax.imageio,org.w3c.dom"; version="${detect.java.version}", \
javax.imageio.plugins.bmp; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.plugins.jpeg; uses:="javax.imageio"; version="${detect.java.version}", \
javax.imageio.plugins.tiff; version="${detect.java.version}", \
javax.imageio.spi; uses:="javax.imageio,javax.imageio.metadata,javax.imageio.stream"; version="${detect.java.version}", \
javax.imageio.stream; uses:="javax.imageio"; version="${detect.java.version}", \
javax.lang.model; uses:="javax.lang.model.element"; version="${detect.java.version}", \
javax.lang.model.element; uses:="javax.lang.model,javax.lang.model.type"; version="${detect.java.version}", \
javax.lang.model.type; uses:="javax.lang.model,javax.lang.model.element"; version="${detect.java.version}", \
javax.lang.model.util; uses:="javax.annotation.processing,javax.lang.model,javax.lang.model.element,javax.lang.model.type"; version="${detect.java.version}", \
javax.management; uses:="javax.management.loading,javax.management.openmbean"; version="${detect.java.version}", \
javax.management.loading; uses:="javax.management"; version="${detect.java.version}", \
javax.management.modelmbean; uses:="javax.management,javax.management.loading"; version="${detect.java.version}", \
javax.management.monitor; uses:="javax.management"; version="${detect.java.version}", \
javax.management.openmbean; uses:="javax.management"; version="${detect.java.version}", \
javax.management.relation; uses:="javax.management"; version="${detect.java.version}", \
javax.management.remote; uses:="javax.management,javax.security.auth"; version="${detect.java.version}", \
javax.management.remote.rmi; uses:="javax.management,javax.management.loading,javax.management.remote,javax.naming,javax.rmi,javax.rmi.CORBA,javax.rmi.ssl,javax.security.auth,org.omg.CORBA,org.omg.CORBA.portable,org.omg.CORBA_2_3.portable"; version="${detect.java.version}", \
javax.management.timer; uses:="javax.management"; version="${detect.java.version}", \
javax.naming; uses:="javax.naming.spi"; version="${detect.java.version}", \
javax.naming.directory; uses:="javax.naming"; version="${detect.java.version}", \
javax.naming.event; uses:="javax.naming,javax.naming.directory"; version="${detect.java.version}", \
javax.naming.ldap; uses:="javax.naming,javax.naming.directory,javax.naming.event,javax.net.ssl"; version="${detect.java.version}", \
javax.naming.spi; uses:="javax.naming,javax.naming.directory"; version="${detect.java.version}", \
javax.net; version="${detect.java.version}", \
javax.net.ssl; uses:="javax.net,javax.security.auth.x500,javax.security.cert"; version="${detect.java.version}", \
javax.print; uses:="javax.print.attribute,javax.print.attribute.standard,javax.print.event"; version="${detect.java.version}", \
javax.print.attribute; version="${detect.java.version}", \
javax.print.attribute.standard; uses:="javax.print.attribute"; version="${detect.java.version}", \
javax.print.event; uses:="javax.print,javax.print.attribute"; version="${detect.java.version}", \
javax.rmi; uses:="javax.rmi.CORBA,org.omg.CORBA"; version="${detect.java.version}", \
javax.rmi.ssl; uses:="javax.net,javax.net.ssl"; version="${detect.java.version}", \
javax.script; version="${detect.java.version}", \
javax.security.auth; version="${detect.java.version}", \
javax.security.auth.callback; version="${detect.java.version}", \
javax.security.auth.kerberos; uses:="javax.crypto,javax.security.auth"; version="${detect.java.version}", \
javax.security.auth.login; uses:="javax.security.auth,javax.security.auth.callback"; version="${detect.java.version}", \
javax.security.auth.spi; uses:="javax.security.auth,javax.security.auth.callback,javax.security.auth.login"; version="${detect.java.version}", \
javax.security.auth.x500; uses:="javax.security.auth"; version="${detect.java.version}", \
javax.security.cert; version="${detect.java.version}", \
javax.security.sasl; uses:="javax.security.auth.callback"; version="${detect.java.version}", \
javax.sound.midi; uses:="javax.sound.midi.spi"; version="${detect.java.version}", \
javax.sound.midi.spi; uses:="javax.sound.midi"; version="${detect.java.version}", \
javax.sound.sampled; uses:="javax.sound.sampled.spi"; version="${detect.java.version}", \
javax.sound.sampled.spi; uses:="javax.sound.sampled"; version="${detect.java.version}", \
javax.sql; uses:="javax.transaction.xa"; version="${detect.java.version}", \
javax.sql.rowset; uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi"; version="${detect.java.version}", \
javax.sql.rowset.serial; uses:="javax.sql.rowset"; version="${detect.java.version}", \
javax.sql.rowset.spi; uses:="javax.naming,javax.sql,javax.sql.rowset"; version="${detect.java.version}", \
javax.swing; uses:="javax.accessibility,javax.print,javax.print.attribute,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.plaf.metal,javax.swing.table,javax.swing.text,javax.swing.text.html,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.border; uses:="javax.swing"; version="${detect.java.version}", \
javax.swing.colorchooser; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.text"; version="${detect.java.version}", \
javax.swing.event; uses:="javax.swing,javax.swing.table,javax.swing.text,javax.swing.tree,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.filechooser; uses:="javax.swing"; version="${detect.java.version}", \
javax.swing.plaf; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.filechooser,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.basic; uses:="javax.accessibility,javax.sound.sampled,javax.swing,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.synth,javax.swing.table,javax.swing.text,javax.swing.text.html,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.nimbus; uses:="javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.synth"; version="${detect.java.version}", \
javax.swing.plaf.metal; uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.multi; uses:="javax.accessibility,javax.swing,javax.swing.filechooser,javax.swing.plaf,javax.swing.text,javax.swing.tree"; version="${detect.java.version}", \
javax.swing.plaf.synth; uses:="javax.swing,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.table,javax.swing.text,javax.swing.tree,javax.xml.parsers,org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.swing.table; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf"; version="${detect.java.version}", \
javax.swing.text; uses:="javax.accessibility,javax.print,javax.print.attribute,javax.swing,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.tree,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.text.html; uses:="javax.accessibility,javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf,javax.swing.text,javax.swing.undo"; version="${detect.java.version}", \
javax.swing.text.html.parser; uses:="javax.swing.text,javax.swing.text.html"; version="${detect.java.version}", \
javax.swing.text.rtf; uses:="javax.swing.text"; version="${detect.java.version}", \
javax.swing.tree; uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.plaf,javax.swing.plaf.basic"; version="${detect.java.version}", \
javax.swing.undo; uses:="javax.swing,javax.swing.event"; version="${detect.java.version}", \
javax.tools; uses:="javax.annotation.processing,javax.lang.model,javax.lang.model.element"; version="${detect.java.version}", \
javax.transaction.xa; version="${detect.java.version}", \
javax.xml; version="${detect.java.version}", \
javax.xml.catalog; version="${detect.java.version}", \
javax.xml.crypto; uses:="javax.xml.crypto.dsig.keyinfo"; version="${detect.java.version}", \
javax.xml.crypto.dom; uses:="javax.xml.crypto,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.crypto.dsig; uses:="javax.xml.crypto,javax.xml.crypto.dsig.keyinfo,javax.xml.crypto.dsig.spec"; version="${detect.java.version}", \
javax.xml.crypto.dsig.dom; uses:="javax.xml.crypto,javax.xml.crypto.dom,javax.xml.crypto.dsig,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.crypto.dsig.keyinfo; uses:="javax.xml.crypto"; version="${detect.java.version}", \
javax.xml.crypto.dsig.spec; uses:="javax.xml.crypto"; version="${detect.java.version}", \
javax.xml.datatype; uses:="javax.xml.namespace"; version="${detect.java.version}", \
javax.xml.namespace; version="${detect.java.version}", \
javax.xml.parsers; uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
javax.xml.stream; uses:="javax.xml.namespace,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform"; version="${detect.java.version}", \
javax.xml.stream.events; uses:="javax.xml.namespace,javax.xml.stream"; version="${detect.java.version}", \
javax.xml.stream.util; uses:="javax.xml.namespace,javax.xml.stream,javax.xml.stream.events"; version="${detect.java.version}", \
javax.xml.transform; version="${detect.java.version}", \
javax.xml.transform.dom; uses:="javax.xml.transform,org.w3c.dom"; version="${detect.java.version}", \
javax.xml.transform.sax; uses:="javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.xml.sax.ext"; version="${detect.java.version}", \
javax.xml.transform.stax; uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.transform"; version="${detect.java.version}", \
javax.xml.transform.stream; uses:="javax.xml.transform"; version="${detect.java.version}", \
javax.xml.validation; uses:="javax.xml.transform,javax.xml.transform.stream,org.w3c.dom,org.w3c.dom.ls,org.xml.sax"; version="${detect.java.version}", \
javax.xml.xpath; uses:="javax.xml.namespace,org.xml.sax"; version="${detect.java.version}", \
jdk.dynalink.beans; version="${detect.java.version}", \
jdk.dynalink.linker.support; version="${detect.java.version}", \
jdk.dynalink.linker; version="${detect.java.version}", \
jdk.dynalink.support; version="${detect.java.version}", \
jdk.dynalink; version="${detect.java.version}", \
jdk.javadoc.doclet; version="${detect.java.version}", \
jdk.jfr.consumer; version="${detect.java.version}", \
jdk.jfr; version="${detect.java.version}", \
jdk.jshell.execution; version="${detect.java.version}", \
jdk.jshell.spi; version="${detect.java.version}", \
jdk.jshell.tool; version="${detect.java.version}", \
jdk.jshell; version="${detect.java.version}", \
jdk.management.jfr; version="${detect.java.version}", \
jdk.nashorn.api.scripting; version="${detect.java.version}", \
jdk.nashorn.api.tree; version="${detect.java.version}", \
jdk.net; version="${detect.java.version}", \
jdk.nio; version="${detect.java.version}", \
jdk.security.jarsigner; version="${detect.java.version}", \
jdk.swing.interop; version="${detect.java.version}", \
netscape.javascript; version="${detect.java.version}", \
org.ietf.jgss; version="${detect.java.version}", \
org.w3c.dom; version="${detect.java.version}", \
org.w3c.dom.bootstrap; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.ls; uses:="org.w3c.dom,org.w3c.dom.events"; version="${detect.java.version}", \
org.w3c.dom.events; uses:="org.w3c.dom,org.w3c.dom.views"; version="${detect.java.version}", \
org.w3c.dom.views; version="${detect.java.version}", \
org.w3c.dom.traversal; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.ranges; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.css; uses:="org.w3c.dom,org.w3c.dom.stylesheets,org.w3c.dom.views"; version="${detect.java.version}", \
org.w3c.dom.html; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.stylesheets; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.w3c.dom.xpath; uses:="org.w3c.dom"; version="${detect.java.version}", \
org.xml.sax; version="${detect.java.version}", \
org.xml.sax.ext; uses:="org.xml.sax,org.xml.sax.helpers"; version="${detect.java.version}", \
org.xml.sax.helpers; uses:="org.xml.sax"; version="${detect.java.version}", \
sun.misc; version="${detect.java.version}", \
sun.reflect; version="${detect.java.version}"
@@ -0,0 +1,26 @@
#
# 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.
#
scanning.package.includes = com.opensymphony.xwork2, \
org.apache.commons.lang3, \
org.apache.struts2, \
ognl, \
freemarker, \
org.apache.velocity, \
javax.servlet;2.5
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-oval-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-pell-multipart-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-plexus-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-plugins</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-portlet-tiles-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-portlet-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-rest-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-sitegraph-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.22</version>
<version>2.5.25</version>
</parent>
<artifactId>struts2-sitemesh-plugin</artifactId>

Some files were not shown because too many files have changed in this diff Show More