I am writing an application that uses Equinox as my OSGi framework. I am trying to discover all of the bundles that are known at the time that my bundle is registered. I attempted to use the following line to retrieve all of the available bundles. However,
EclipseStarter.getSystemBundleContext().getBundles();
gives me a warning of...
Discouraged access: The method getSystemBundleContext() from the type EclipseStarter is not accessible due to restriction on required library D:\java\eclipse\plugins\org.eclipse.osgi_3.4.0.v20080605-1900.jar
What is the proper usage to get a list of all of the available bundles within the framework?
You could use your own bundle context as an entry point instead of the EclipseStarter - in your plugin activator:
start(BundleContext context)
{
context.getBundles(); // what you want
}
Look at the Plugin cans AbstractUIPlugin classes if you don't know about them.
If you really need singleton access, your plugin is probably one - feel free to expose YourPlugin.getInstance().getBundleContext().
(Disclaimer: I haven't tried it - but it would be consistent with OSGi/Eclipse)
Related
I am looking for a possibility to activate a OSGi Bundle form another running bundle, is it possible ?
Thanks,
Ashok
Yes, this is the concept of a management agent in the specification. You will need a Bundle Context object. There are several ways of getting this context:
Highly Recommended: Using DS, you can declare a BundleContext object as parameter in the activate method
Not perfectly safe: Use FrameworkUtil.getBundle(Class).getBundleContext()
Only use when DS is not an option: Use an activator, a BundleContext object is passed
The BundleContext has a method Bundle installBundle(String). You can pass a URL to the JAR file that holds the bundle. To start the bundle you can all start on it.
Since you're likely not well versed in OSGi you might want to take a look at Apache Felix FileInstall. This allows you to drop bundles in a directory that are then automatically installed. Though the API is simple, managing an OSGi framework has a lot of complicated cases due to the interaction with the rest of the world.
I'm looking to create a Java 'library' (JAR) that can be used within other projects, but I'd like my library to be extensible. I'm after something OSGi-esque, where my library has 'extension points' that other JARs can hook into. The thinking is that my library will have a core set of methods that will be called by the projects it's used in, but the exact implementation of these methods might vary based on the project/context.
I've looked into OSGi (e.g. Equinox), but I'm not sure it can be used in the way I'm after. It seems to be geared towards standalone apps rather than creating a library.
What would be the best way of achieving this? Can OSGi be used in this way, and if not are there frameworks out there that will do this?
I hope all that's clear - I have a clear idea of what I want, but it's hard to articulate.
OSGi is great, but I don't think that this is what you need. By using OSGi (-Services), you force the user of your library to use an OSGi environment, too.
I think as #Peter stated, you can do this by simply extending classes of your library in the specific project / context.
But in case you want to use OSGi, there is a simple way to achieve this. It's called Bundle Fragments. This way you can create a bundle and extend a so-called Host-Bundle", i.e. your library, without altering the original library. A popular use case for this is if you have platform specific code in your bundles.
What you are naming a Java library is named "Bundle" in OSGi context.
OSGi Bundle is a JAR file with some special Meta-Information in its MANIFEST.MF file. Now, every OSGi Bundle have either Exported-Packages or Imported-Packages.
Through Export-Packages Manifest header, you can show what all packages you are exporting.. And your other project can simply add the package it wants to use from them to its Import-Packages..
Here's an example: -
Bundle A Manifest: -
Export-Packages: com.demo.exported;
Bundle B Manifest: -
Import-Packages: com.demo.exported;version=(1.0.0, 2.0.0]
This way your bundle B (A different project) can call the methods from the class in the package that it imported from Bundle A..
Now, the version you see in the import-package, is just to show what all package version can it accept.. You can have 2 bundles with two different implementation of some interfaces and provide this package in two different version.. Both will be available..
Till now, I was talking about Static data-types..
You can also have your services exposed dynamically through Declarative Service.. In this case you will have to define one XML file (Component Definition) where you show what all services your Bundle will expose.. And in the other bundle, you can again define another XML, to show what all services it requires..
These are called, Provided Services and Referenced Services..
I think this will give you a little idea about what can be done.
And if I am wrong somewhere in interpreting your problem please specify the same..
*NOTE: - And of course OSGi is used for creating independent Bundles, that can be re-used in other projects.. They bring Modularity to your project..
As others have mentioned, you don't need OSGi or any framework for this. You can do this my employing patterns like the template method pattern or the strategy pattern. There are several other patterns for dynamically modifying/extending functionality, but these seem to fit your description most. They do not require any framework.
The benefit you would get from a framework like OSGi would be that it would manage the wiring for you. Normally, you'll have to write some code that glues your libraries and the extensions together - with a framework like OSGi, this will not be automated with minimal overhead (in case of OSGi, the overhead is some entries in the JAR-manifest).
This is a biggie.
I have a well-structured yet monolithic code base that has a primitive modular architecture (all modules implement interfaces yet share the same classpath). I realize the folly of this approach and the problems it represents when I go to deploy on application servers that may have different conflicting versions of my library.
I'm dependent on around 30 jars right now and am mid-way though bnding them up. Now some of my modules are easy to declare the versioned dependencies of, such as my networking components. They statically reference classes within the JRE and other BNDded libraries but my JDBC related components instantiate via Class.forName(...) and can use one of any number of drivers.
I am breaking everything up into OSGi bundles by service area.
My core classes/interfaces.
Reporting related components.
Database access related components (via JDBC).
etc....
I wish for my code to be able to still be used without OSGi via single jar file with all my dependencies and without OSGi at all (via JARJAR) and also to be modular via the OSGi meta-data and granular bundles with dependency information.
How do I configure my bundle and
my code so that it can
dynamically utilize any driver on the
classpath and/or within the OSGi
container environment
(Felix/Equinox/etc.)?
Is there a run-time method to detect if I am running in an OSGi container that is compatible across containers (Felix/Equinox/etc.) ?
Do I need to use a different class loading mechanism if I am in a OSGi container?
Am I required to import OSGi classes into my project to be able to load an at-bundle-time-unknown JDBC driver via my database module?
I also have a second method of obtaining a driver (via JNDI, which is only really applicable when running in an app server), do I need to change my JNDI access code for OSGi-aware app servers?
Utilizing any driver within the OSGi environment requires you using a DynamicImport-Package: * statement so your bundle can resolve these packages when you load a driver with Class.forName(..).
Probably the easiest way is to try to access a class that is in the org.osgi.framework package. Those should at least be always around in an OSGi environment (see snippet below). There are more sophisticated mechanisms, so let me know if you need something more advanced. Also, take a look at the OSGi R4.2 core spec, paragraph 3.8.9 which shows some methods of finding the Bundle and BundleContext of a class and therefore indirect helps in determining if you're in a framework or not.
That depends on what you're doing, no generic "yes" or "no" answer here. OSGi uses classloaders and does so in a way that is not "typical" for a standard Java application, but depending on what you're doing, you might not notice.
No.
Take a look at the recently released OSGi enterprise specs. They have a chapter on JNDI integration in OSGi which probably allows you to leave your code (largely) unmodified.
A simple example snippet:
public static boolean inOSGi() {
try {
Class.forName("org.osgi.framework.FrameworkUtil");
return true;
}
catch (ClassNotFoundException e) {
return false;
}
}
Just make sure that you if you put this code in a bundle, the bundle should import org.osgi.framework (otherwise it will never find that class).
I made a JDBC driver manager for OSGI in an Eclipse RCP and I will take you through how to play nice with OSGI. First, forget about DynamicImport-Package, the only good way to use OSGI is to install/start/stop bundles and use the OSGI mechanism the way it was designed.
You have your JDBC bundle, and create another "Driver bundle" which has the initialization of the DriverClass, the Connection logic and add the necessary commons libraries such as dbcp2 and pool2.
Export the Driver bundle as a JAR/ZIP and include it in your JDBC bundle as a resource.
Let your JDBC bundle unzip the Driver bundle in its work area.
String workdir= Platform.getStateLocation(jdbc_bundle).toPortableString();
Programmatically add driver jars and modify the Driver bundle's MANIFEST.MF file accordingly.
Load the Driver bundle programmatically from the work area
getBundleContext().installBundle("file:/"+workdir);
Use bundle.start(), stop(), uninstall() as necessary when programmatically modifying the list of drivers.
The pax-jdbc can be used to delegate dataSources via declarative way, means you can create a config entry in ConfigAdmin service, and the dataSource can be accessed via JNDI. The JDBC driver is deployed as bundle. (most of them have OSGi version)
For example:
The config entry PID is org.ops4j.datasource-test
Properties:
osgi.jdbc.driver.name=H2
databaseName=test
user=sa
password=
dataSourceName=testds-h2
The service is identified by the given dataSourceName. So you can filter for it with (&(objectClass=javax.sql.DataSource)(dataSourceName=test2)).
And you can access the datasource via JNDI:
osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=test2)
I have successfully managed to start Apache Felix from code and register an own Bundle.
Following relation between OSGI-projects is needed:
[OsgiInterface] -- provides interfaces.
[OsgiModuleA] -- (bundle) provides an implementation of those interfaces.
knows [OsgiInterface]
[OsgiUsage] -- makes use of one or more bundle.
knows [OsgiInterface] and [OsgiModuleA]
Now I have problems registering a service which implements an interface.
I would guess that my entries in manifest.mf files are wrong.
Additional information
It would be very kind, if someone could look at the code in my previous question
Let me refer to this question:
I tried to create a third project OsgiInterfaces, which provides an interface SomeInterface in the package interfaces. This project is known by both OsgiModuleA and OsgiUsage.
OsgiModuleA: manifest.mf has now an additional value interfaces for the entry Import-Package:. Furthermore, there is an instance of SomeInterface provided to the activator.
When the bundle is started, an NoClassDefFoundError occurs: the interface SomeInterface is not known.
EDIT:
Now, that the error is fixed, I can tell, that the most important part was:
map.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,
"my.interfaces; version=1.0.0");
Without this, I got ClassCastException.
In the most basic form, services are registered in Java code, not using manifest or any other file. This usually happens in your BundleActivator.
Long i = new Long(20); // the service implementation
Hashtable props = new Hashtable();
props.put("description", "This an long value");
bundleContext.registerService(Long.class.getName(), i, props);
I suggest you read a tutorial, like the one at Knopflerfish
An alternative is using Declarative Services, or the new Blueprint facility. Using either of these (or other non-standardized systems) you will declare your services in a (usually XML) file, instead of writing code to interact with the services registry.
But you should probably figure out the fundamentals manually first.
[OsgiUsage] -- makes use of one or more bundle.
knows [OsgiInterface] and [OsgiModuleA]
It should not be necessary for the bundle that uses a service to know about the bundle that provides it. Both of them just need to know the service interface. In fact, bundles should not need to know about other bundles at all. They only need to import packages, and consume or provide services.
I understand that you have SomeInterface in another bundle, right? Then you must also export that package in that bundle's manifest, eg.
Export-Bundle: interfaces
But you really should have a look at the bnd tool mentioned in another answer. This generates standard OSGi manifests.
I suggest you look at the iPOJO project. This make using Felix much easier.
https://felix.apache.org/documentation/subprojects/apache-felix-ipojo.html
I would say use bnd directly or maven-bundle-plugin to create OSGI enabled jars.
It's easier than writing the OSGI manifest yourself(typos, mistakes, missing imports/exports)
Trying wrapping the jars with bnd as a start.
Inside my host application I tried implement a simple pushService, which
shall be used to transfer an instance of a class named Vehicle to the OSGi
world, by providing a set and get method. To be able to use the service I
exported both the service interface and the Vehicle class to a jar file and
imported that file within the bundle, which should use the service.
Everytime I tried to use the Vehicle class within my host application,
which instanciates the felix framework, and the bundle, I got a linkage
error. After reading the following blog entry
(http://frankkieviet.blogspot.com/2009/03/javalanglinkageerror-loader-constraint.html)
I understood why this error occurs. But I have no clue how to solve my problem.
Is it possible to share a class between the host application and an OSGi
instance? Maybe I have to use reflection instead of import the jar file? I had a look at that library (http://code.google.com/p/transloader/) and I'm don't really sure whether this lib is able to solve my problem or not ...
BR,
Markus
At one time I was using Felix to do EXACTLY what you're asking in a custom client-server application. I've since switched to Equinox (they correctly implement framework fragments which I needed for swing LAF as osgi bundles). I THINK the following will work in Felix, I KNOW it works in Equinox.
UPDATE: I started down a very similar path with my host application. I realized early that I needed to move as much code as possible into real OSGi bundles to truly take advantage of the platform. My host application sets up client/server comms and synchronizes bundles; that's it. The few classes I used to share have been moved into bundle and I haven't look back. If you design/application can support having the majority of code in bundles I would definitely go that route. Even if some redesign is required, it's worth it.
Before initializing the OSGi runtime, set this property "org.osgi.framework.system.packages" to include you packages (no wildcards) separated by semi-colons ";". You may additionally need to include the base osgi packages, "org.osgi.framework" and the base services "org.osgi.packageadmin", "org.osgi.startlevel", "org.osgi.url".
I just dug through my version control and found a snippet when I was still using Felix (the setup is almost the same for Equinox)
Map<String, String> configMap = new HashMap<String, String();
configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
"your.package;other.package;org.osgi.framework");
// setup other properties
Bundle systemBundle = new Felix(configMap, null);
systemBundle.start();
// Now you can use classes from "your.package" with explicity
// declaring them as imports in bundles