Skip to content

Commit

Permalink
Add abstraction support for the code executed inside the OSGi Framework
Browse files Browse the repository at this point in the history
Currently the code for executing a build in the embedded framework is
non trivial as well.

This now uses an abstract class here as well and add support for the
EclipseFramework to use such one level indirection as well.
  • Loading branch information
laeubi committed Jan 19, 2025
1 parent 82559f8 commit 15823d2
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public record Bundles(Set<String> bundles) {
public static final String BUNDLE_API_TOOLS = "org.eclipse.pde.api.tools";
public static final String BUNDLE_ECLIPSE_HELP_BASE = "org.eclipse.help.base";
public static final String BUNDLE_PDE_CORE = "org.eclipse.pde.core";
public static final String BUNDLE_JDT_CORE = "org.eclipse.jdt.core";

static final String BUNDLE_LAUNCHING_MACOS = "org.eclipse.jdt.launching.macosx";
static final String BUNDLE_APP = "org.eclipse.equinox.app";
static final String BUNDLE_SCR = "org.apache.felix.scr";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -125,24 +126,32 @@ private static String toBundleState(int state) {
}

@SuppressWarnings("unchecked")
public <X extends Callable<R> & Serializable, R extends Serializable> R execute(X runnable)
throws InvocationTargetException {
public <X extends Callable<R> & Serializable, R extends Serializable> R execute(X runnable,
String... requireBundles) throws InvocationTargetException {
try {
start();
byte[] runnableBytes = getBytes(runnable);
BundleContext bundleContext = framework.getBundleContext();
String newBundleId = connector.newBundle(runnable.getClass());
Class<?> clazz = runnable.getClass();
String newBundleId = connector.newBundle(clazz, requireBundles);
Bundle bundle = bundleContext.installBundle(newBundleId);
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && !Object.class.equals(superclass)) {
if (!Objects.equals(EclipseModuleConnector.getLocationFromClass(clazz),
EclipseModuleConnector.getLocationFromClass(superclass))) {
bundleContext.installBundle(connector.newFragment(superclass, bundle));
}
}
try {
bundle.start();
Class<?> foreignClass = bundle.loadClass(runnable.getClass().getName());
Class<?> foreignClass = bundle.loadClass(clazz.getName());
Object foreignObject = readObject(runnableBytes, foreignClass.getClassLoader());
Method method = foreignClass.getMethod("call");
byte[] resultBytes = getBytes(method.invoke(foreignObject));
if (resultBytes == null) {
return null;
}
return (R) readObject(resultBytes, runnable.getClass().getClassLoader());
return (R) readObject(resultBytes, clazz.getClassLoader());
} finally {
bundle.uninstall();
connector.release(newBundleId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -28,8 +29,10 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleException;
import org.osgi.framework.connect.ConnectContent;
Expand Down Expand Up @@ -64,7 +67,7 @@ public Optional<BundleActivator> newBundleActivator() {
return Optional.empty();
}

public String newBundle(Class<?> clazz) {
public String newBundle(Class<?> clazz, String[] requireBundles) {
URI location = getLocationFromClass(clazz);
if (location == null) {
throw new RuntimeException("can't get location of class " + clazz);
Expand All @@ -75,6 +78,24 @@ public String newBundle(Class<?> clazz) {
header.put(Constants.BUNDLE_SYMBOLICNAME, id);
header.put(Constants.BUNDLE_VERSION, "1.0.0");
header.put(Constants.DYNAMICIMPORT_PACKAGE, "*");
if (requireBundles != null && requireBundles.length > 0) {
header.put(Constants.REQUIRE_BUNDLE, Arrays.stream(requireBundles).collect(Collectors.joining(",")));
}
modules.put(id, new TempBundle(new File(location), header));
return id;
}

public String newFragment(Class<?> clazz, Bundle bundle) {
URI location = getLocationFromClass(clazz);
if (location == null) {
throw new RuntimeException("can't get location of class " + clazz);
}
String id = "eclipse-fragment" + UUID.randomUUID().toString();
Map<String, String> header = new HashMap<>();
header.put(Constants.BUNDLE_NAME, clazz.getName());
header.put(Constants.BUNDLE_SYMBOLICNAME, id);
header.put(Constants.BUNDLE_VERSION, "1.0.0");
header.put(Constants.FRAGMENT_HOST, bundle.getSymbolicName());
modules.put(id, new TempBundle(new File(location), header));
return id;
}
Expand All @@ -83,7 +104,7 @@ public void release(String id) {
modules.remove(id);
}

private static URI getLocationFromClass(Class<?> clazz) {
static URI getLocationFromClass(Class<?> clazz) {
ProtectionDomain domain = clazz.getProtectionDomain();
if (domain == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
/*******************************************************************************
* Copyright (c) 2023 Christoph Läubrich and others.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.eclipsebuild;

import java.io.IOException;
Expand All @@ -28,78 +16,42 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;

public class EclipseBuild implements Callable<EclipseBuildResult>, Serializable {

/**
* Abstract class for performing a build and producing a result
*
* @param <Result>
*/
public abstract class AbstractEclipseBuild<Result extends EclipseBuildResult>
implements Callable<Result>, Serializable, IProgressMonitor {
private boolean debug;
private String baseDir;

EclipseBuild(Path projectDir, boolean debug) {
protected AbstractEclipseBuild(Path projectDir, boolean debug) {
this.debug = debug;
this.baseDir = pathAsString(projectDir);
}

@Override
public EclipseBuildResult call() throws Exception {
EclipseBuildResult result = new EclipseBuildResult();
public final Result call() throws Exception {
Platform.addLogListener((status, plugin) -> debug(status.toString()));
disableAutoBuild();
deleteAllProjects();
IProject project = importProject();
IProgressMonitor debugMonitor = new IProgressMonitor() {

@Override
public void worked(int work) {

}

@Override
public void subTask(String name) {
debug("SubTask: " + name);
}

@Override
public void setTaskName(String name) {
debug("Task: " + name);
}

@Override
public void setCanceled(boolean value) {

}

@Override
public boolean isCanceled() {
return false;
}

@Override
public void internalWorked(double work) {

}

@Override
public void done() {

}

@Override
public void beginTask(String name, int totalWork) {
setTaskName(name);
}
};
project.build(IncrementalProjectBuilder.CLEAN_BUILD, debugMonitor);
project.build(IncrementalProjectBuilder.FULL_BUILD, debugMonitor);
project.build(IncrementalProjectBuilder.CLEAN_BUILD, this);
project.build(IncrementalProjectBuilder.FULL_BUILD, this);
Result result = createResult(project);
for (IMarker marker : project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)) {
result.addMarker(marker);
debug(marker.toString());
}
ResourcesPlugin.getWorkspace().save(true, new NullProgressMonitor());
ResourcesPlugin.getWorkspace().save(true, this);
return result;
}

protected abstract Result createResult(IProject project) throws Exception;

static void disableAutoBuild() throws CoreException {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceDescription desc = workspace.getDescription();
Expand All @@ -109,7 +61,7 @@ static void disableAutoBuild() throws CoreException {

private void deleteAllProjects() throws CoreException {
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
project.delete(IResource.NEVER_DELETE_PROJECT_CONTENT | IResource.FORCE, new NullProgressMonitor());
project.delete(IResource.NEVER_DELETE_PROJECT_CONTENT | IResource.FORCE, this);
}
}

Expand All @@ -119,14 +71,13 @@ private IProject importProject() throws CoreException, IOException {
IProjectDescription projectDescription = ResourcesPlugin.getWorkspace()
.loadProjectDescription(projectDescriptionFile);
projectDescription.setLocation(projectPath);
// projectDescription.setBuildSpec(new ICommand[0]);
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectDescription.getName());
project.create(projectDescription, new NullProgressMonitor());
project.open(new NullProgressMonitor());
project.create(projectDescription, this);
project.open(this);
return project;
}

private void debug(String string) {
protected void debug(String string) {
if (debug) {
System.out.println(string);
}
Expand All @@ -139,4 +90,51 @@ static String pathAsString(Path path) {
return null;
}

@Override
public boolean isCanceled() {
return Thread.currentThread().isInterrupted();
}

@Override
public void beginTask(String name, int totalWork) {
if (name != null && !name.isBlank()) {
debug("> " + name);
}
}

@Override
public void subTask(String name) {
if (name != null && !name.isBlank()) {
debug(">> " + name);
}
}

@Override
public void setTaskName(String name) {
if (name != null && !name.isBlank()) {
debug("> " + name);
}
}

@Override
public void done() {
// do nothing
}

@Override
public void setCanceled(boolean value) {
// do nothing
}

@Override
public void internalWorked(double work) {
// do nothing

}

@Override
public void worked(int work) {
// do nothing
}

}
Loading

0 comments on commit 15823d2

Please sign in to comment.