Skip to content

Commit

Permalink
Initial attempt to fix OSGi Plugin issues discovered during examinati…
Browse files Browse the repository at this point in the history
…on of

WW-5075.
- Draws on PR#427 and utilizes similar code, but updated for JDK8.
- Updated OSGi plugin dependencies to OSGi R7.
- 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.6.x.
- Applied changes to the OSGi Admin Bundle to function under 2.6.x.
- Applied changes to the OSGi Demo Bundle to function under 2.6.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.
- BundlePackageLoader changes suggested by L. Lenart (from PR#427).
- 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.
- FelixOsgIHost test modified to not fail if felix-cache directory
  bundle load issues arise (warn only) to avoid failing the whole build.
  • Loading branch information
JCgH4164838Gh792C124B5 committed Aug 21, 2020
1 parent 6d865b6 commit cb3ca4b
Show file tree
Hide file tree
Showing 44 changed files with 4,127 additions and 705 deletions.
2 changes: 1 addition & 1 deletion bundles/admin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<instructions>
<manifestLocation>META-INF</manifestLocation>
<Struts2-Enabled>true</Struts2-Enabled>
<Import-Package>*,com.opensymphony.xwork2</Import-Package>
<Import-Package>*</Import-Package>
<Export-Package>org.apache.struts2.osgi.admin*</Export-Package>
<Spring-Context>*;create-asynchronously:=false</Spring-Context>
</instructions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public String view() {
}

public String start() throws BundleException {
clearErrorsAndMessages();
addActionMessage("Start - OSGi Host: " + osgiHost + ", ID: " + id);

Bundle bundle = osgiHost.getBundles().get(id);
try {
bundle.start();
Expand All @@ -69,29 +72,35 @@ public String start() throws BundleException {
//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();
Expand All @@ -114,14 +123,14 @@ public Bundle getBundle() {
}

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)
pkgs.add(packageConfig);
}
bundleAccessor.getPackagesByBundle(bundle).stream().map(
name -> configuration.getPackageConfig(name)).filter(
packageConfig -> (packageConfig != null)).forEachOrdered(packageConfig -> {
pkgs.add(packageConfig);
});
}
return pkgs;
}
Expand All @@ -132,16 +141,14 @@ public ArrayList<String> getHeaderKeys() {

public Collection<Bundle> getBundles() {
List<Bundle> bundles = new ArrayList(osgiHost.getBundles().values());
Collections.sort(bundles, new Comparator<Bundle>() {
public int compare(Bundle bundle1, Bundle bundle2) {
boolean bundle1StrutsEnabled = isStrutsEnabled(bundle1);
boolean bundle2StrutsEnabled = isStrutsEnabled(bundle2);

if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled))
return bundle1.getSymbolicName().compareTo(bundle2.getSymbolicName());
else {
return bundle1StrutsEnabled ? -1 : 1;
}
Collections.sort(bundles, (Bundle bundle1, Bundle bundle2) -> {
boolean bundle1StrutsEnabled = isStrutsEnabled(bundle1);
boolean bundle2StrutsEnabled = isStrutsEnabled(bundle2);

if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled)) {
return bundle1.getSymbolicName().compareTo(bundle2.getSymbolicName());
} else {
return bundle1StrutsEnabled ? -1 : 1;
}
});
return bundles;
Expand Down Expand Up @@ -172,17 +179,21 @@ public boolean isAllowedAction(Bundle bundle, String val) {
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;
}

if ("start".equals(val)) {
return state == Bundle.RESOLVED;
} else if ("stop".equals(val)) {
return state == Bundle.ACTIVE;
} else if ("update".equals(val)) {
return state == Bundle.ACTIVE || state == Bundle.INSTALLED
|| state == Bundle.RESOLVED;
if (val != null) {
switch (val) {
case "start":
return state == Bundle.RESOLVED;
case "stop":
return state == Bundle.ACTIVE;
case "update":
return state == Bundle.ACTIVE || state == Bundle.INSTALLED || state == Bundle.RESOLVED;
default:
break;
}
}
throw new IllegalArgumentException("Invalid state");
}
Expand All @@ -197,6 +208,7 @@ public void setBundleAccessor(BundleAccessor bundleAccessor) {
this.bundleAccessor = bundleAccessor;
}

@Override
public void withServletContext(ServletContext servletContext) {
osgiHost = (OsgiHost) servletContext.getAttribute(StrutsOsgiListener.OSGI_HOST);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,29 @@
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();
Expand All @@ -52,7 +61,9 @@ public String execute() {
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();
Expand All @@ -75,17 +86,59 @@ public String getOutput() {
}

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
}
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;
}
}
26 changes: 22 additions & 4 deletions bundles/admin/src/main/resources/JQUERY-LICENSE.txt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
5 changes: 3 additions & 2 deletions bundles/admin/src/main/resources/NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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/).
6 changes: 3 additions & 3 deletions bundles/admin/src/main/resources/osgi/admin/shell.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
9 changes: 6 additions & 3 deletions bundles/admin/src/main/resources/osgi/admin/viewBundle.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -163,5 +163,8 @@
</table>
</div>
</div>

<@s.actionmessage />

</body>
</html>
12 changes: 9 additions & 3 deletions bundles/admin/src/main/resources/osgi/admin/viewBundles.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand All @@ -44,6 +44,9 @@
</a>
</div>
</div>

<@s.actionerror />

<table class="properties" style="clear:both; width:700px">
<thead>
<tr>
Expand Down Expand Up @@ -87,5 +90,8 @@
</#list>
</tbody>
</table>

<@s.actionmessage />

</body>
</html>
Loading

0 comments on commit cb3ca4b

Please sign in to comment.