I would like my bundle to be either configurable via System properties or via the OSGi Compendium Config Admin.
I am compiling my bundle against the org.osgi:osgi.cmpn:6.0.0 bundle, which as made clear in the OSGi Alliance blog is meant to be used at compile-time only, with the framework providing the actual implementations at runtime.
My code obviously needs to use the ConfigAdmin package (to handle the case in which the ConfigAdmin Service is present)... which means that if the runtime does not export the ConfigAdmin package, my bundle will not resolve properly.
But I wanted this resolution to be optional... so I added this to the manifest:
org.osgi.service.cm;resolution:=optional;version="[1.5,2)"
Now, the bundle will resolve but will crash at runtime with a java.lang.NoClassDefFoundError: org/osgi/service/cm/ManagedService even if the user will not actually use ConfigAdmin for configuration. So this forces the user to install the config-admin bundle just to make my bundle work.
I guess the secret here is to not instantiate any classes that force the JVM to load a class that uses the org.osgi.service.cm package... but I can't see how I can achieve that without ugly hacks with reflection...
Does anyone know how I can check if the package is available at runtime, and if not, avoid the java.lang.NoClassDefFoundError at runtime, making this package dependency truly optional?
The way to tell if the package is available at runtime is to attempt to load a class from it and be prepared for the NoClassDefFoundError. You could do this in a central place and then decide to avoid code paths which require the optional but absent package.
The PackageAdmin can be used to inspect package metadata of the system. It has been deprecated and you are expected to use the BundleWiring instead.
So, in your DS, you would have a dependency to the PackageAdmin/BundleWiring and check if the cm package is exported...
Related
I'm running Apache Felix as a bundle loader inside an Android app.
Since the Jaca SecurityManager isn't accessible there, I'm looking for a solution to prevent the bundles from accessing certain packages, like java.io.*.
The idea of just writing a custom classloader that will return null or throw an exception when such a class is requested seems the best, however, I can't find how to set a global classloader for all bundles managed by Felix.
Try setting this property:
org.osgi.framework.system.packages - Specifies a comma-delimited list
of packages that should be exported via the System Bundle from the
framework class loader. The framework will set this to a reasonable
default. If the value is specified, it replaces any default value.
By default, all java.* classes are visible to any bundle. If you override this, you can change that behaviour. Bundles trying to import the packages you did not include (such as java.io) will not be resolved during installation and hence will not be able to start.
See this for more information: http://felix.apache.org/site/apache-felix-framework-configuration-properties.html
[Clarification] Forgive the lack of clarity in the initial description. Allow me to re-phrase the question.
Does there exist a way to perform runtime compilation using the javax.tools API, usable in OSGi (again stressing runtime), which understands a bundle's dependencies and security constraints?
[update]
Please see https://github.com/rotty3000/phidias
It's a well formed OSGi bundle.
The readme provides all the details of the very tiny 4 class API (8k module).
In order to get from a set of package imports and exports to a list of bundles which can be used for compilation, you'll need some sort of repository of candidate bundles, and a provisioner to work out which bundles best provide which packages. If you're using 'Require-Bundle' (not a best practice), you'll know the bundle names, but not necessarily the versions, so some provisioning is still required.
For example, in Eclipse PDE, the target platform is used as the basic repository for compilation. You can also do more sophisticated things like using Eclipse's p2 provisioning to provision your target platform, so you can use an external p2 repository as your repository instead of setting one up yourself. For command line builds, Tycho allows Maven builds to use the same sort of mechanisms for resolving the classpath as Eclipse itself uses.
An alternative approach is to list your 'classpath' as Maven dependencies, and let the maven bundle plugin (based on bnd) generate your manifest for you.
If you can't take advantage of existing command line tools because you're compiling programatically (it's not entirely clear from your question what problem you're trying to solve), your best best is probably to take advantage of an existing provisioning technology, like OBR, Eclipse p2, or Apache Ace to work out the bundles which should be on the class path for compilation.
This is exactly what we do in bndtools ... If I had a bit of time I would add a compiler to bnd so it could also do this.
Sure you can, you just have to write a custom JavaFileManager which will supply the right classes to compile against to the JavaCompiler.
For example you can write one that gets its classes from an OSGi runtime. If you don't mind having a dependency from your compiler bundle to the libraries you need then it's pretty easy, otherwise you can use the wiring api to look to other bundles as well. (OSGi 4.3+ only). If you intercept which packages it requests while compiling you can generate Package-Import statements so you can generate a bundle.
I made a rough GitHub example a few months back:
https://github.com/flyaruu/test-dynamic-compiler
There were some issues (I couldn't get the Eclipse ecj compiler to work for example, I didn't look into bundle security at all, and due to the dynamic nature of OSGi you have to listen to bundle changes to update your compilation path.), but it works fine.
I've so far found that the real answer is "No there is not!"
The predominant runtime compilation scenario currently for java is JSP compilation. An investigation of the app servers I've had the occasion to review use one of these methods:
invocation of javac (through a system call)
use of ecj/jdt
uses javax.tools in a non-OSGi aware way
All of these approaches are based on collecting the available classpath by directly introspecting jars or classes in the file system.
None of the current approaches are aware of OSGi characteristics like the dynamic nature of the environment or the underlying restrictions imposed of the framework itself.
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
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)