Does anyone have a "hello world" sample or tutorial for creating an Eclipse plugin fragment?
I have a working host plugin that, for the sake of simplicity, is just this...
public void start(BundleContext context) throws Exception {
System.out.println("Hello....");
super.start(context);
plugin = this;
}
public void stop(BundleContext context) throws Exception {
System.out.println("Goodbye...");
plugin = null;
super.stop(context);
}
Simple enough and works. Now I want to add a fragment to that host, which seems not as simple as creating a plugin host. I just don't see how to create a fragment project and add logic to it. Let's say I just want to do something simple and have the fragment to print a "Hello2" at start() and "Goodbye2" at stop(). Can someone give me a working example?
Eclipse -> File -> New... -> Fragment project -> set the host plugin (which is either in your workspace or in plugins in target platform).
Open Plugin manifest editor (you can do it by clicking on build.properties, manifest.mf or fragment.xml - if there is no such a file, create it by hand)
In tab Extentions click Add.. and add org.eclipse.ui.startup and browse class which implements org.eclipse.ui.IStartup class.
Create this class and implement it. You need to implement method earlyStartup() which is entry point to the fragment.
Note: The lines below are just example. I didn't test it so there might be errors...
All you need is this (this is project structure / directory structure):
Fragment-Project - root dir
/META-INF
MANIFEST.MF
/src (which is source directory)
FragmentStartClass.java (which implement org.eclipse.ui.IStartup interface and earlyStartup method)
build.properties
fragment.xml
META-INF/MANIFEST.MF content:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: FragmentProject
Bundle-SymbolicName: FragmentProject;singleton:=true
Bundle-Version: 1.0.0
Bundle-ClassPath: src/,.
Fragment-Host: *HostPluginProjectSymbolicName*;bundle-version="1.0.0"
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Require-Bundle:
build.properties content:
source.. = src,\
output.. = bin/
bin.includes = META-INF/,
.,
fragment.xml
fragment.xml content:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<fragment>
<extension
point="org.eclipse.ui.startup">
<startup
class="FragmentStartClass">
</startup>
</extension>
</fragment>
FragmentStartClass.java content:
import org.eclipse.ui.IStartup;
public class FragmentStartClass implements IStartup {
public void earlyStartup() {
System.out.println("Hello World From Fragment!");
}
}
Well, first off what are you trying to accomplish? Are you sure fragments are the solution?
Fragments are simply additions to an existing plugin. There is no Bundle-Activator to "startup" the fragment. You just gain the additional resources in the fragment. You could use an extension point to inform the host that some particular functionality was extended by a present fragment. Think of it as merging the two at runtime.
See this note from the Eclipse Wiki.
I'm not sure if you can do this without the host plugin knowing something about loading the fragment's extra implementation code.
For example:
MyInterface o = null;
try {
Class<?> c = getClass().getClassLoader().loadClass("my.fragment.Activator");
o = (MyInterface) c.newInstance();
} catch (ClassNotFoundException e) {
}
if (o != null) {
o.start();
}
where MyInterface is an interface defined in the host plugin, and implemented by your fragment.
Another way might be to have the fragment provide an extension point identifying the extended class, but that is probably more than is needed.
I think what you are trying to do is better served by using extension points and separate plugins, as you want to add functionality to the base plugin.
Fragments are only used to add resources or extra classes to the base plugin.
For the record, I just created an Eclipse Fragment that contains some PMD Java Rule classes.
Next to the class files, there is only one file required in that Jar: META-INF/MANIFEST.MF:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: com.acme.tools.pmd
Bundle-SymbolicName: com.acme.tools.pmd;singleton:=true
Bundle-Version: 1.0.0
Fragment-Host: net.sourceforge.pmd.eclipse.plugin;bundle-version="3.2.6"
Bundle-RequiredExecutionEnvironment: J2SE-1.5
I tried this with current Eclipse 3.7. Deployment is by placing the Jar file into <eclipse root>/dropins/plugins (create that folder if it's not yet there).
Related
I have a workspace in Eclipse RCP 3.8.2 IDE with 3 plugins and 2 fragments:
P1 and P2 plugins
BONECP1 fragment (P1 is the HOST)
BONECP2 fragment (P2 is the HOST)
MAIN plugin (requires P1 and P2)
Life goes well running MAIN plugin inside the IDE... but when exporting MAIN plugin (as an Eclipse RCP Product), fragment BONECP2 causes this error:
Any ideas? Tips:
The fragments are CLONES (classpath libs are the SAME!)
Sometimes (if some config is changed in BONECP1 fragment) BONECP2 fragment cannot find some classes that are loaded and present in the HOST. Reconfiguring BONECP1 fragment (making it not singleton / singleton again) resolves the issue!!
If the operation that caused the error is tried again, then the classes not found previously are found! (exported version and inside the IDE)
Removing the HOST MINIMUM VERSION of BONECP2 fragment resolves the EXPORT error... but then BONECP2 fragment cannot find some classes that are loaded and present in the HOST!
Something is missing in my understanding of fragments? Why is eclipse OK with this configuration inside the IDE and not at export time?
Here is the MANIFEST of the BONECP fragments:
Manifest-Version: 1.0
Eclipse-BundleShape: jar
Bundle-ManifestVersion: 2
Bundle-Name: com.jolbox.bonecp
Bundle-SymbolicName: BONECP1;singleton:=true
Bundle-Version: 0.7.0.qualifier
Fragment-Host: P1;bundle-version="1.0.0.qualifier"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ClassPath: .,
libraries/bonecp-0.7.0.jar,
libraries/guava-r08.jar,
libraries/slf4j-api-1.6.1.jar,
libraries/slf4j-log4j12-1.6.1.jar
Export-Package: com.jolbox.bonecp
I'm playing with OSGi framework extension bundles in order to fully understand them.
After looking at the OSGi R6 specification (3.15 and 4.2.4.1) I've sucessfully invoked the "start" method of the ExtensionBundleActivator. Now I'm trying to register a service inside such activator. However, when I trying to consume such service, the reference annotation fails to connect the service.
Here's my code ('ve changed the name of the bundle, but it shouldn't matter):
public class ExtensionBundleActivator implements BundleActivator {
#Override
public void start(BundleContext context) throws Exception {
System.out.println("start extension bundle activator!");
context.registerService(
BundleExample.class.getName(),
new BundleExampleImpl(),
new Hashtable<>(new HashMap<>()));
}
#Override
public void stop(BundleContext context) throws Exception {
//service automatically unregistered
}
}
And here's the manifest of such extension bundle:
Manifest-Version: 1.0
Bnd-LastModified: 1476436248622
Build-Jdk: 1.8.0_91
Built-By: massi
Bundle-ClassPath: .
Bundle-ManifestVersion: 2
Bundle-Name: extensionbundleexample
Bundle-SymbolicName: com.massimobono.microsi.extensionbundleexample
Bundle-Version: 0.0.1.SNAPSHOT
Conditional-Package: com.massimobono.microsi.common.*;
Created-By: Apache Maven Bundle Plugin
ExtensionBundle-Activator: com.massimobono.microsi.bundleexample.imp
l.ExtensionBundleActivator
Fragment-Host: system.bundle; extension:=framework
Provide-Capability: osgi.service;objectClass:List<String>="com.massimobo
no.microsi.bundleexample.BundleExample"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Service-Component: OSGI-INF/com.massimobono.microsi.bundleexample.im
pl.ExtensionBundleExample.xml
Tool: Bnd-3.0.0.201509101326
The consuming bundle (part of the bundle):
#Reference(cardinality=ReferenceCardinality.OPTIONAL)
public BundleExample actualBundleExample;
#Activate
public void activate() {
System.out.println("activating " + this.getClass().getSimpleName() + "with actual bundle set to "+ this.actualBundleExample);
}
My question is: What am I doing wrong? Why the consumer can't detect the registered service of the extension bundle? Or maybe I'm just doing something the framework forbids... in this case is it impossible to provide a service from an extension bundle? How can I interact with the code within the extension bundle (aka accessing something inside the extension bundle itself)?
Here some notes:
I'm using felix as OSGi implementation;
Both "ExtensionBundleExample" and "BundleExample" are loaded inside the auto-process folder of felix (the default one is "bundle" but I tweaked the config.properties to use "corebundles" folder;
The output corretly show "start extension bundle activator!" but when it's time to display the reference of actualBundleExample, the output shows "null";
the optional cardinality of BundleExample is used for testing purposing: I just wanted to invoke the "acivator" method the consumer component has (in order to see the System.out.println console;
from my previous question I understand extension bundles are a niche inside the OSGi framework, but I want to understand them nonetheless: I find the lack of examples on the internet regarding this topic quite annoying;
Thanks for any reply!
The primary purpose of extension bundles is for framework extensions, absolutely not for regular usage. I.e. extension bundles are often tightly coupled to a framework. The reason is that many rules do not count for extension bundles because they are on the "wrong" side of the fence. There are few examples for very good reasons. You should not use them unless you really know what you're doing because most of the OSGi rules do not apply.
That said. My expectation is that the package you use for the BundleExample differs between the extension bundle (comes from the class path) and the DS example exported by some bundle. Since they come from different class loaders OSGi considers them different services because you would get a class loader exception when you tried to use it.
You can solve this by letting the framework export this package.
Just a guess.
I am working on an RCP application which is based on eclipse plugins. In one of my plugin project I add another plugin project as dependency. Let say Project A has Project B as a dependency defined under its manifest. Project B contains jackcess.jar file as referenced library.
In Project B I have a class called Mirror.java
public Mirror(String source, String template, String target) throws SQLException, IOException {
this.sourceString=source;
this.templateFileString=template;
this.targetFileString=target;
}
inside from project A when I try to create an object of class Mirror
Mirror m = new Mirror(connectionString, "EABase_JET4_empty.eap",platformDB.getAbsolutePath());
I get the following error
java.lang.NoClassDefFoundError:
com/healthmarketscience/jackcess/ImportFilter
build.properties of Project B (containing jackcess.jar)
bin.includes = META-INF/,\
src/main/resources/lib/jackcess-1.2.6.af3.jar
The MANIFEST.MF
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: MirrorDbToEap
Bundle-SymbolicName: MirrorDbToEap
Bundle-Version: 1.0.0
Export-Package: .,
com.example.jetdb.mirror
Require-Bundle: CommonsIo;bundle-version="2.0.0",
org.apache.commons.lang;bundle-version="2.6.0",
org.apache.commons.logging;bundle-version="1.0.4"
Anyone having any idea what's going wrong here?
Thanks
You are not including the jackcess.jar in the bin.includes in the build.properties file so it is not being included in the RCP build.
Open the build.properties editor and select the jar in the 'Binary Build' selection.
The jar must also appear in the Bundle-ClassPath in the MANIFEST.MF. In the manifest editor in the Runtime tab add the jar to the 'Classpath' section (you should also have '.' for normal plugin code).
It seems the class which is available in compile time, is not available in run time.
So, let's see, I have three bundles: Provider interface, Provider implementation and Client.
Everything is running smoothly on Eclipse, but when I export the bundles and run em, the following error appears when I try to retrieve the Provider Service:
java.lang.NoClassDefFoundError: provider/providerinterface/ProviderService
The interface is exporting his package, so there should be no errors with that. Alongside, Client is also importing the providerInterface package in its manifest.
The .jar of the Provider interface bundle does have the ProviderService class inside, so there are no error exporting either.
Provider Interface's manifest:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: IMA_Provider
Bundle-SymbolicName: IMA_Provider
Bundle-Version: 1.0.0
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Export-Package: provider.providerinterface
Client's manifest:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: MAClient
Bundle-SymbolicName: MA_Client
Bundle-Version: 1.0.0
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: provider.providerinterface,
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.4.2"
Provider and Client are just symbolic names, the "Client" bundle imports and uses a lot of other classes from many other bundles with no errors whatsoever. Anyway, I'm getting stuck with this one, and I can't find any reason.
Any help?
Usually there is something wrong with the build.properties file, causing the bundles to miss the actual class files. This does not matter when running directly from Eclipse, but it does matter when you export the bundles using PDE.
Things to check:
Are the classfiles really in the bundles? When checking the bundle, you should have at least at toplevel a provider/ folder and a META-INF/ folder
the build.properties file should look something like:
output.. = bin/
source.. = src/
bin.includes = META-INF/,.
Good luck, Frank
I was finally able to solve this.
There were no issues with the Manifest files or the build.properties, this was a constructor problem.
The MA_Provider implementation was lacking a void constructor; Once I added this
public ProviderImpl(){}
on the implementation class of the ProviderInterface, the OSGi bundles were able to retrieve the service.
Situation: Open source OSGI framework SMILA (http://www.eclipse.org/smila/) started as Windows Service with the aid of Apache commons-daemon (http://commons.apache.org/daemon/). Trying to load a DLL via System.loadLibrary() from OSGI bundle while Manifest.mf includes Bundle-NativeCode: path/to/dll.
Environment: Windows Server 2003, Java 1.6
Error: During the invocation of System.loadLibrary() the complete Java process hangs. When the service is stopped System.loadLibrary() finish and code execution goes on until the OSGI framework shut down.
The error doesn’t occur on Windows Server 2008 or if the OSGI framework isn’t started as service.
The DLL itself is stripped down to no functionality for testing. All imports are static and the only depended library is kernel32.ddl.
Could anyone imagine why this is happening and how to fix it?
Manifest containing DLL:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: NTFS Utils Acl Win32 Library
Bundle-SymbolicName: com.eccenca.utils.ntfs.acl.win32
Bundle-Version: 2.2.0
Bundle-Vendor: brox IT-Solutions GmbH
Fragment-Host: com.eccenca.utils.ntfs
Eclipse-PlatformFilter: (& (osgi.os=win32) (osgi.arch=x86))
Bundle-NativeCode: ntfsacl/Release/NtfsAcl.dll
Bundle-RequiredExecutionEnvironment: JavaSE-1.6+
Manifest containing code:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: NTFS Utils Acl
Bundle-SymbolicName: com.eccenca.utils.ntfs
Bundle-Version: 2.2.0
Bundle-Vendor: brox IT-Solutions GmbH
Export-Package: com.eccenca.processing.acl,
com.eccenca.utils.ntfs
Import-Package: org.apache.commons.io;version="1.4.0",
org.apache.commons.lang,
org.apache.commons.logging;version="1.1.1",
org.eclipse.smila.blackboard;version="0.8.0",
org.eclipse.smila.datamodel,
org.eclipse.smila.processing;version="0.8.0",
org.eclipse.smila.processing.pipelets;version="0.8.0",
org.eclipse.smila.utils.config;version="0.8.0",
org.eclipse.smila.utils.service;version="0.8.0",
org.osgi.framework;version="1.4.0"
SMILA-Pipelets: com.eccenca.processing.acl.AccessListConverterPipelet
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Code snipped with System.loadLibrary() invocation:
public class ACLList {
private static final org.apache.commons.logging.Log LOG =
org.apache.commons.logging.LogFactory.getLog(ACLList.class);
static {
try {
LOG.debug("Start loading library");
System.loadLibrary("NtfsAcl");
if (LOG.isInfoEnabled()) {
LOG.info("NTFS ACL library was succesfully loaded");
}
} catch (Throwable e) {
LOG.error(e);
}
}
private ACLList() {
}
public static native ArrayList<ACLEntry> getAccessFor(String path,
String serverName) throws IOException;
}
There are two possible issues with the situation you described; I don't know exactly how Equinox handles native code, so I'll just present them to you both.
Bundle-NativeCode requires at least one parameter
You use a Bundle-NativeCode header that just defines a library, and it seems you use Eclipse-PlatformFilter to specify what that library is intended for. Section 3.10 of the spec shows that you need at least one parameter for the library to be selected.
You can change your Bundle-NativeCode header to read
Bundle-NativeCode: ntfsacl/Release/NtfsAcl.dll;osname=win32
and your bundle will be able to find the right library.
Load only from your own bundle
Judging from your code, it could be possible that you define the library in one bundle, and try to load it in another; that doesn't work, a bundle can only load a library that it contains itself.