Skip to content

Commit

Permalink
improve loading bundles
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeloffner committed Dec 16, 2024
1 parent aae86c6 commit be98bdd
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 55 deletions.
1 change: 1 addition & 0 deletions core/src/main/java/lucee/commons/io/SystemUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,7 @@ public static double getLoaderVersion() {
loaderVersion = Version.VERSION;
}
catch (Throwable t) {
t.printStackTrace();
ExceptionUtil.rethrowIfNecessary(t);
try {
// Get a lookup object
Expand Down
15 changes: 6 additions & 9 deletions loader/src/main/java/lucee/loader/engine/CFMLEngineFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -789,13 +789,12 @@ public Felix getFelix(final File cacheRootDir, Map<String, Object> config) throw
}
}

final StringBuilder sb = new StringBuilder("Loading felix with config:");
final Iterator<Entry<String, Object>> it = config.entrySet().iterator();
Entry<String, Object> e;
while (it.hasNext()) {
e = it.next();
sb.append("\n- ").append(e.getKey()).append(':').append(e.getValue());
}
/*
* final StringBuilder sb = new StringBuilder("Loading felix with config:"); final
* Iterator<Entry<String, Object>> it = config.entrySet().iterator(); Entry<String, Object> e; while
* (it.hasNext()) { e = it.next();
* sb.append("\n- ").append(e.getKey()).append(':').append(e.getValue()); } System.err.println(sb);
*/

felix = new Felix(config);
try {
Expand All @@ -805,9 +804,7 @@ public Felix getFelix(final File cacheRootDir, Map<String, Object> config) throw
// this could be cause by an invalid felix cache, so we simply delete it and try again
if (!isNew && "Error creating bundle cache.".equals(be.getMessage())) {
Util.deleteContent(cacheRootDir, null);

}

}

return felix;
Expand Down
128 changes: 82 additions & 46 deletions loader/src/main/java/lucee/loader/osgi/BundleLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
import java.util.Map.Entry;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
Expand All @@ -53,7 +53,7 @@ public class BundleLoader {

public static BundleCollection loadBundles(final CFMLEngineFactory engFac, final File cacheRootDir, final File jarDirectory, final File rc, final BundleCollection old)
throws IOException, BundleException {
// if (rc.getName().toLowerCase().toLowerCase().indexOf("ehcache") != -1)

final JarFile jf = new JarFile(rc);// TODO this should work in any case, but we should still improve this code
try {
// Manifest
Expand All @@ -62,7 +62,6 @@ public static BundleCollection loadBundles(final CFMLEngineFactory engFac, final
final Attributes attrs = mani.getMainAttributes();
// default properties
final Properties defProp = loadDefaultProperties(jf);

// read the config from default.properties
final Map<String, Object> config = new HashMap<>();
{
Expand Down Expand Up @@ -99,8 +98,10 @@ public static BundleCollection loadBundles(final CFMLEngineFactory engFac, final

// load Required/Available Bundles
final Map<String, String> requiredBundles = readRequireBundle(rb); // Require-Bundle

final Map<String, String> requiredBundleFragments = readRequireBundle(rbf); // Require-Bundle-Fragment
final Map<String, File> availableBundles = loadAvailableBundles(jarDirectory);

final Map<String, File> availableBundles = loadAvailableBundles(jarDirectory, requiredBundles, requiredBundleFragments);

// deploys bundled bundles to bundle directory
// deployBundledBundles(jarDirectory, availableBundles);
Expand Down Expand Up @@ -134,78 +135,112 @@ public static BundleCollection loadBundles(final CFMLEngineFactory engFac, final

public static List<Bundle> addRequiredBundles(Map<String, String> requiredBundles, Map<String, File> availableBundles, boolean always, CFMLEngineFactory engFac,
BundleContext bc) {
List<Bundle> bundles = new ArrayList<>();
final List<Bundle> bundles = new ArrayList<>();
Iterator<Entry<String, String>> it = requiredBundles.entrySet().iterator();

// Use regular threads
ExecutorService executor = createExecutorService(requiredBundles.size());
List<Future<Bundle>> futures = new ArrayList<>();

List<CompletableFuture<?>> futures = new ArrayList<>();
while (it.hasNext()) {
Entry<String, String> e = it.next();
futures.add(executor.submit(() -> {
String id = e.getKey() + "|" + e.getValue();
File f = always ? null : availableBundles.get(id);
if (f == null) {
f = engFac.downloadBundle(e.getKey(), e.getValue(), null);
futures.add(CompletableFuture.runAsync(() -> {
try {
String id = e.getKey() + "|" + e.getValue();
File f = always ? null : availableBundles.get(id);
if (f == null) {
f = engFac.downloadBundle(e.getKey(), e.getValue(), null);
}
bundles.add(BundleUtil.addBundle(engFac, bc, f, null));
}
catch (Exception ex) {
ex.printStackTrace();
}
return BundleUtil.addBundle(engFac, bc, f, null);
}));
}

for (Future<Bundle> future: futures) {
try {
bundles.add(future.get());
}
catch (Exception ex) {
ex.printStackTrace();
}
}

executor.shutdown();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
return bundles;
}

private static Map<String, File> loadAvailableBundles(final File jarDirectory) {
private static Map<String, File> loadAvailableBundles(final File jarDirectory, final Map<String, String> requiredBundles, final Map<String, String> requiredBundleFragments) {
final Map<String, File> rtn = new ConcurrentHashMap<>();
final File[] jars = jarDirectory.listFiles();

if (jars != null && jars.length > 0) {
// Create a thread pool with a fixed number of threads
// ExecutorService executor = Executors.newFixedThreadPool(Math.min(jars.length,
// Runtime.getRuntime().availableProcessors()));
ExecutorService executor = createExecutorService(jars.length);
try {
List<Future<?>> futures = new ArrayList<>();
for (File jar: jars) {
if (!jar.isFile() || !jar.getName().endsWith(".jar")) continue;

// Submit tasks for processing each jar file
futures.add(executor.submit(() -> {
// we first try to make a match based on the required bundles and bundle fragments
List<File> remainings = null;
boolean has;
for (File jar: jars) {
if (!jar.isFile() || !jar.getName().endsWith(".jar")) continue;
String fullname = null;
has = false;
try {
fullname = jar.getName();
fullname = jar.getName().substring(0, fullname.length() - 4);
int lastDot = fullname.lastIndexOf('.');
if (lastDot != -1) {
int lastSlash = fullname.substring(0, lastDot).lastIndexOf('-');
if (lastSlash != -1) {
String name = fullname.substring(0, lastSlash);
String version = fullname.substring(lastSlash + 1);
String val;
if ((val = requiredBundles.get(name)) != null && val.equals(version)) {
rtn.put(name + "|" + version, jar);
has = true;
}
else if ((val = requiredBundleFragments.get(name)) != null && val.equals(version)) {
rtn.put(name + "|" + version, jar);
has = true;
}
}
}

if (!has) {
if (remainings == null) remainings = new ArrayList<>();
remainings.add(jar);
}
}
catch (Throwable t) {
Util.rethrowIfNecessary(t);
}
}
if (remainings != null) {

if (remainings.size() > 1) {
List<CompletableFuture<?>> futures = new ArrayList<>();
for (File jar: remainings) {
// Submit tasks for processing each jar file
futures.add(CompletableFuture.runAsync(() -> {
long start = System.currentTimeMillis();
try {
rtn.put(loadBundleInfo(jar), jar);
}
catch (IOException ioe) { // Log the exception
new Exception("Error loading bundle info for [" + jar.toString() + "]", ioe).printStackTrace();
}
}));
}
// Wait for all tasks to complete
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
else {
for (File jar: remainings) {
try {
rtn.put(loadBundleInfo(jar), jar);

}
catch (IOException ioe) {
// Log the exception
new Exception("Error loading bundle info for [" + jar.toString() + "]", ioe).printStackTrace();
}
}));
}
// Wait for all tasks to complete
for (Future<?> future: futures) {
try {
future.get();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
finally {
executor.shutdown(); // Ensure the executor is properly shut down
}
}
return rtn;

}

private static Map<String, File> loadAvailableBundlesSerial(final File jarDirectory) {
Expand Down Expand Up @@ -241,7 +276,8 @@ public static ExecutorService createExecutorService(int maxThreads) {
if (e instanceof ThreadDeath) throw (ThreadDeath) e;
}
}
return Executors.newFixedThreadPool(maxThreads);

return Executors.newFixedThreadPool(Math.min(Runtime.getRuntime().availableProcessors(), maxThreads));
}

private static int parseJavaVersion(String version) {
Expand Down

0 comments on commit be98bdd

Please sign in to comment.