Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoProcessor - Load and start bundles in parallel #236

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 87 additions & 49 deletions main/src/main/java/org/apache/felix/main/AutoProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@

import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.osgi.framework.*;
import org.osgi.service.startlevel.*;

Expand Down Expand Up @@ -89,9 +95,17 @@ public class AutoProcessor
**/
public static void process(Map configMap, BundleContext context)
{
configMap = (configMap == null) ? new HashMap() : configMap;
processAutoDeploy(configMap, context);
processAutoProperties(configMap, context);
ExecutorService executor = null;
try {
executor = Executors.newFixedThreadPool(8);
Map configMapLocal = (configMap == null) ? new HashMap() : configMap;
processAutoDeploy(executor, configMapLocal, context);
processAutoProperties(executor, configMapLocal, context);
} finally {
if (executor != null) {
executor.shutdown();
}
}
}

/**
Expand All @@ -100,7 +114,8 @@ public static void process(Map configMap, BundleContext context)
* specified deploy actions.
* </p>
*/
private static void processAutoDeploy(Map configMap, BundleContext context)
private static void processAutoDeploy(ExecutorService executor, Map configMap,
BundleContext context)
{
// Determine if auto deploy actions to perform.
String action = (String) configMap.get(AUTO_DEPLOY_ACTION_PROPERTY);
Expand Down Expand Up @@ -156,53 +171,57 @@ private static void processAutoDeploy(Map configMap, BundleContext context)
// Look in the specified bundle directory to create a list
// of all JAR files to install.
File[] files = new File(autoDir).listFiles();
List jarList = new ArrayList();
List<File> jarList = new ArrayList<>();
if (files != null)
{
Arrays.sort(files);
for (int i = 0; i < files.length; i++)
for (File file : files)
{
if (files[i].getName().endsWith(".jar"))
if (file.getName().endsWith(".jar"))
{
jarList.add(files[i]);
jarList.add(file);
}
}
}

// Install bundle JAR files and remember the bundle objects.
final List startBundleList = new ArrayList();
for (int i = 0; i < jarList.size(); i++)
final List<Future<Bundle>> startBundleList = new ArrayList<>();
for (File jarFile : jarList)
{
// Look up the bundle by location, removing it from
// the map of installed bundles so the remaining bundles
// indicate which bundles may need to be uninstalled.
Bundle b = (Bundle) installedBundleMap.remove(
((File) jarList.get(i)).toURI().toString());
Bundle b = (Bundle) installedBundleMap.remove(jarFile.toURI().toString());
Future<Bundle> futureBundle = CompletableFuture.completedFuture(b);

try
{
// If the bundle is not already installed, then install it
// if the 'install' action is present.
if ((b == null) && actionList.contains(AUTO_DEPLOY_INSTALL_VALUE))
{
b = context.installBundle(
((File) jarList.get(i)).toURI().toString());
String jarPath = jarFile.toURI().toString();
b = context.installBundle(jarFile.toURI().toString());
futureBundle = executor.submit(() -> {
try {
return context.installBundle(jarPath);
} catch (BundleException ex) {
System.err.println("Auto-deploy install: "
+ ex + ((ex.getCause() != null) ? " - "
+ ex.getCause() : ""));
return null;
}
});
}
// If the bundle is already installed, then update it
// if the 'update' action is present.
else if ((b != null) && actionList.contains(AUTO_DEPLOY_UPDATE_VALUE))
{
b.update();
}

// If we have found and/or successfully installed a bundle,
// then add it to the list of bundles to potentially start
// and also set its start level accordingly.
if ((b != null) && !isFragment(b))
{
startBundleList.add(b);
sl.setBundleStartLevel(b, startLevel);
}
startBundleList.add(futureBundle);
}
catch (BundleException ex)
{
Expand Down Expand Up @@ -238,16 +257,31 @@ else if ((b != null) && actionList.contains(AUTO_DEPLOY_UPDATE_VALUE))
// action is present.
if (actionList.contains(AUTO_DEPLOY_START_VALUE))
{
for (int i = 0; i < startBundleList.size(); i++)
for (Future<Bundle> bundleFuture : startBundleList)
{
try
{
((Bundle) startBundleList.get(i)).start();
}
catch (BundleException ex)
{
System.err.println("Auto-deploy start: "
+ ex + ((ex.getCause() != null) ? " - " + ex.getCause() : ""));
try {
Bundle bundle = bundleFuture.get();
int startLvl = startLevel;
executor.execute(() -> {
try
{
if (bundle != null) {
if (!isFragment(bundle)) {
// set its start level accordingly.
sl.setBundleStartLevel(bundle, startLvl);
bundle.start();
}
}
}
catch (BundleException ex)
{
System.err.println("Auto-deploy start: "
+ ex + ((ex.getCause() != null) ? " - "
+ ex.getCause() : ""));
}
});
} catch (InterruptedException | ExecutionException e) {
System.err.println("Problem getting bundle: " + e.getMessage());
}
}
}
Expand All @@ -260,7 +294,8 @@ else if ((b != null) && actionList.contains(AUTO_DEPLOY_UPDATE_VALUE))
* specified configuration properties.
* </p>
*/
private static void processAutoProperties(Map configMap, BundleContext context)
private static void processAutoProperties(ExecutorService executor,
Map configMap, BundleContext context)
{
// Retrieve the Start Level service, since it will be needed
// to set the start level of the installed bundles.
Expand All @@ -276,9 +311,9 @@ private static void processAutoProperties(Map configMap, BundleContext context)
// property name, where "n" is the desired start level for the list
// of bundles. If no start level is specified, the default start
// level is assumed.
for (Iterator i = configMap.keySet().iterator(); i.hasNext(); )
for (Object keyObj : configMap.keySet())
{
String key = ((String) i.next()).toLowerCase();
String key = ((String) keyObj).toLowerCase();

// Ignore all keys that are not an auto property.
if (!key.startsWith(AUTO_INSTALL_PROP) && !key.startsWith(AUTO_START_PROP))
Expand Down Expand Up @@ -315,35 +350,38 @@ private static void processAutoProperties(Map configMap, BundleContext context)
{
System.err.println("Auto-properties install: " + location + " ("
+ ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")");
if (ex.getCause() != null)
ex.printStackTrace();
if (ex.getCause() != null)
ex.printStackTrace();
}
}
}

// Now loop through the auto-start bundles and start them.
for (Iterator i = configMap.keySet().iterator(); i.hasNext(); )
for (Object keyObj : configMap.keySet())
{
String key = ((String) i.next()).toLowerCase();
String key = ((String)keyObj).toLowerCase();
if (key.startsWith(AUTO_START_PROP))
{
StringTokenizer st = new StringTokenizer((String) configMap.get(key), "\" ", true);
for (String location = nextLocation(st); location != null; location = nextLocation(st))
{
// Installing twice just returns the same bundle.
try
{
Bundle b = context.installBundle(location, null);
if (b != null)
String loc = location;
executor.execute(() -> {
try
{
b.start();
// Installing twice just returns the same bundle.
Bundle b = context.installBundle(loc, null);
if (b != null)
{
b.start();
}
}
}
catch (Exception ex)
{
System.err.println("Auto-properties start: " + location + " ("
+ ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")");
}
catch (Exception ex)
{
System.err.println("Auto-properties start: " + loc + " ("
+ ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")");
}
});
}
}
}
Expand Down