I am learning OSGi framework. What i found that Bundles consists of .class files and manifest file. Now suppose I have two bundles A and B and bundle B depends on bundle A's service. So while compiling bundle B i have to keep Bundle A's service class in the classpath of bundle B or have to keep it(Bundle A's service) inside bundle B so that compiler can compile. Now if we are doing so then how modularity is achieved and how the two bundles are independent. Please help.
Thanks
Use interfaces!!
You have in interface I. Class A implements I and therefore depends on it, both at compile time and at runtime. Class B uses an instance of I, which it obtains as an OSGi Service. Therefore B depends on I at compile time and at runtime.
Therefore neither A nor B has direct knowledge of each other. Implementation code is hidden, only the interface is visible and shared. And that's how modularity is achieved.
Manually doing dependency management is error prone. I suggest you use maven to manage the dependencies. In addition, maven-bundle-pluginIn could help you check import/export bundle at compile time; while maven-eclipse-plugin help you generate eclipse projects according to the dependency configuration in pom.
Related
Having hard time to describe this and I bet this is something very simple, but I just can't google sollution.
I am using many modules in my project. For simple argument let's say I have modules A and B.
B depends on A.
When I add dependency to external library (using implementation keyword) in module A to use some of it's code in the module, I cannot access library's code in project B. How can I achieve that? I would like A to be my "base" project with all dependencies in that place rather than having to repeat myself in other modules, that depend on it.
The implementation configuration means the dependencies are internal (implementation specific) for the project and should not be exposed on the compilation classpath of other dependent projects. This helps encapsulate dependencies and speeds up the build as you don't need to recompile dependent projects if you only change internal dependencies.
If you want to expose them, you need to use the api configuration instead, along with the java-library plugin.
I am trying to understand the intended use case for Bundle-Classpath in OSGI bundles.
Here is my understanding, please help me understand if this is correct.
Let's say I am working on creating an OSGI bundle which will be deployed in an ecosystem of other bundles. The bundle I am working on needs some other bundles, but they are not loaded/exported in this ecosystem, and I do not have control on what the ecosystem exports. In such a scenario, I can put these bundles inside some directory (say 'lib') which becomes part of my bundle. These bundles should also be referenced from the Bundle-Classpath, so they can be loaded.
Is this a correct use case for Bundle-Classpath ?
Will these additional bundles also be loaded in the OSGI container and will packages exported by them be available to other bundles ?
Bundle-ClassPath is intended for including dependencies in our bundle, so that our bundle can be deployed standalone.
Let's take an example. Suppose the code in my bundle uses a library, e.g. Google Guava. I have two choices for packaging my bundle:
Simply create my bundle with only my own code inside it. The bundle will now have the Import-Package statements that declare a dependency on Guava, and anybody who wants to deploy my bundle into his application will also have to deploy Guava.
Alternatively I can include a copy of Guava inside my bundle and reference it from my Bundle-ClassPath. Whoever deploys my bundle can deploy just my bundle, and doesn't need to worry about where to get Guava from. In fact, the existence of Guava inside my bundle is an implementation detail, and the deployer doesn't even need to know that I am using it.
The choice between these two options is a trade-off. Option 2 has the advantage that my bundle is easier to deploy because it is standalone -- everything it needs is right there inside it. On the other hand my bundle is much bigger than it needs to be, which could become a problem if lots of other bundles also embed their own copy of Guava.
A more severe problem with option 2 is that all of the dependencies of the library now become my dependencies as well. Actually Guava is a rare example of a Java library with no dependencies of its own... but many other Java libraries drag in a huge tree of transitive dependencies. If you use this approach with, say, Hibernate then your own bundle will also have that large dependency set. This gets very ugly, very quickly.
So, you should be cautious not to overuse Bundle-ClassPath/Embed-Dependency. You should only consider using it if the dependency is (a) small, and with no transitive dependencies, and (b) your bundle uses the library as an internal implementation detail, i.e. it is not part of your public API.
UPDATE
I forgot to answer your second question about the exports. The answer is NO, the exports of any "bundles" you put on your Bundle-ClassPath will NOT become exports of your own bundle. In fact the JARs we put on Bundle-ClassPath are not treated as bundles at all, they are just JARs.
You can choose to export packages that come from within the JARs on your Bundle-ClassPath but you have to do this in the MANIFEST.MF of your own bundle.
The most common use case for this header is the packaging of external libraries. Let's say you have some library foo.jar, and want to use its classes in your bundle.
You put the jar into your bundle like so,
/
com/company/Activator.class
foo.jar
META-INF/MANIFEST.MF
In you manifest, you can now use
Bundle-ClassPath: foo.jar,.
Remember to include the . on the classpath, or you will not be able to find the classes in your bundle.
When classes are on the Bundle-ClassPath, you can use them like any other class: use them in your code, or export them.
I think you may be a bit off here.
A Bundle-Classpath is an ordered, comma-separated list of relative
bundle JAR file locations to be searched for class and resource
requests.
What this means is that when some bundle class needs another class in
the same bundle, the entire bundle class path of the containing bundle
is searched to find the class.
From OSGI in Action.
Let's consider a concrete case. Imagine a bundle (JAR file) with the following structure:
src/a/A.class
src2/b/B.class
src3/c/C.class
if you wanted a.A, b.B and c.C to be available to each other, you'd have to define src, src2 and src3 as pertaining to the bundle classpath. That would mean you'd have to add to your manifest file the following line:
Bundle-ClassPath: src,src2,src3
I am trying to prevent dependency checking in java compiler, I use command line compilation,is there any way to tell javac compiler not to check dependency while compiling a java file ?
... is there any way to tell javac compiler not to check dependencies while compiling a java file ?
The simple answer is No.
Suppose you have some class A that wants to call some method m defined by class B. In order to successfully compile A, the compiler needs to know that B is a real class, that it defines the method m, that it has the expected number and type of arguments, what checked exceptions it throws, and what type of value it returns. Without this information about B, the compiler cannot compile A.
And this propagates to the project level. If a class in project P depends on a class in project Q, the compiler must have that class (at least) in order to compile the class in P.
In short, no such compiler option exists, and it is hard to see how it could be implemented it it did.
If you're two projects are dependent on each other then they are really one project and must be built together. If the relationship is a one-way relationship then you will still need to build the dependent project first and then have the results of the project on the classpath when building the second project.
Most IDEs have capabilities to manage this. In Eclipse you can mark that one project depends on another project and the dependent project's output files will be added to the classpath of the other. Typically all dependencies are built and packaged as jars and those jar files are placed onto the classpath when compiling parent projects.
Building code without having access to the dependencies is very difficult and not recommended. In some cases it can be possible. Eclipse has built their own incremental Java compiler so that they do not have to recompile the entire project each time a single file is modified. You can read more about it here but in order to use such a compiler you will likely have to do a lot of work.
UPDATE to reflect your new edit:
In order to build a common library that common library must not depend on any classes in your platform-specific sections. As Peter Rader has mentioned the typical way of doing that is by using interfaces. For example, your common library can have an EventListener interface which receives events. In your platform specific libraries you can implement that interface and process the events according to the specific platform. Since your common library only depends on the EventListener class and not the specific implementations it does not need those specific classes when it compiles.
If you have dependencies, they will always be checked and give warnings, but your classes will be compiled anyway.
Often frameworks offer an api.jar that contains interfaces and enums.
I am very new to OSGi.
I am developing a plugin A (osgi bundle), suppose A which depends on libraries, suppose B-1.0 and C-1.0. Now If the library C-1.0 depends on library B-2.0 (Note: the different version of library B). So my plugin has two different versions of the library B in its classpath. Now, How can I handle this situation ?
As I am studying from last 4-5 days about OSGi that it creates a classloader for each plugin in the JIRA application, so that dependency version mismatch do not occur between plugins. But What would a developer do If a plugin itself needs two different versions of a library jar ?
Can I create two different classloaders in a single osgi bundle through OSGi, say one for package X and another one for package Y ?
Please help me in any of the above scenarios or point me to the right direction.
Thanks in advance.
Remember that bundles do not depend on other bundles!!
Bundles import packages that are exported by other bundles. (unless you have used Require-Bundle, but you should not). So to rephrase the scenario from your example:
Bundle A imports package org.foo. Bundle C exports package org.foo, and OSGi wires the import to the export. So far so good.
Bundle C also imports package org.bar. Bundle B 1.0 exports package org.bar. Therefore OSGi wires these together and everything is still fine.
Now... bundle A also imports package org.wibble. Bundle B 2.0 exports package org.wibble. This is fine as well! Bundles B 1.0 and B 2.0 are simply different bundles as far as OSGi is concerned, they can both be installed at the same time.
So when you look at the dependencies the way they actually work, you find that it's perfectly possible for A to import code that comes from two different versions of B. However there is a limitation. Consider the following:
Bundle D imports packages org.foo and org.bar v1.0 (yes, packages are versioned).
Bundle E exports package org.foo, which satisfies the import in D. Bundle E also imports package org.bar v2.0.
Some other bundles (say F v1 and F v2) export the 2 versions of the org.bar packages.
Actually this scenario can still work. D can import version 1.0 of package org.bar from somewhere, and E can import version 2.0 of package org.bar from somewhere else, at the same time as D is importing package org.foo from E. I personally find this pretty incredible! But it does not work if org.foo "uses" org.bar, where "uses" means that some types in org.bar are visible in the API of org.foo. In this case, bundle D would be exposed to 2 different copies org.bar, which is not allowed, so OSGi will prevent bundle D from running by not allowing it to enter RESOLVED or ACTIVE states.
In osgi bundle or plugin you'll have meta-inf flie which will define which classes you import if you pass extra agrument being the version=2.0 then it will use the class from B-2.0 if you don't specify anything then it'll resolve to one that is loaded by classloader first.
i.e.
import-package(C 1.0):
b.some.package; version="2.0" or b.some.package; version="[2.0,4.0)"
import-package(A 1.0):
b.some.package; version="1.0" or b.some.package; version="1.0"
Hope this helps
Anup
Since each OSGi bundle has its own classloader, there will be 4 bundles in the runtime, and also 4 classloaders (A, B-1.0, B-2.0, C-1.0).
You may have two copies of the same class included in B (one from 1.0 and another from 2.0). If you run this, you may simply run into a ClassCastException in the A code because two versions of B classes are not the same.
OSGi provides a "uses" clause to detect this type of situations early. For example, C may have a uses clauses like the following:
Export-Package: c.some.package;uses="b.some.package";version="1.0"
Import-Package: b.some.package;version="2.0"
In this case, you will have an earlier failure (while resolving A), known as a uses conflict, because C places a constraint for its consumer on an acceptable version of B.
Conceptually, the only way to fix this problem is to have consumers of B (A and C in this case) agree on the version of B.
I'm having a problem reconciling building a project for use within an application server and for use as a stand-alone application.
To give an overall simplified context, say I have three Projects A, B, C.
Project A depends on Project B which depends on Project C.
Project C has a dependency X which is marked as provided since it was expected that it would be available as a JEE library within say an application server. i.e. jms.jar.
So if I perform an assembly build of Project A, I get all the transitive dependencies save for those marked as provided as expected.
Now I have a new deployment scenario where Project A needs to be used in a standalone environment i.e. outside an application server.
So now I need the jms jar to be a compile dependency. Does this mean that I should explicitly add a compile dependency for X in Project A? Doesn't this violate the Law of Demeter, i.e. don't talk to strangers, in the sense Project A shouldn't explicitly know about Project C but only about Project B?
This is a simple example but in reality I have multiple dependencies which have been marked as provided but are now need to be compile or runtime dependencies so they end up in the artifact produced by the maven assembly plugin.
Is this a fundamental problem with Maven or am I not using the tools correctly?
Thanks in advance for any guidance.
If you need your build to have variations in it for different scenarios, you need to use profiles and keep certain things (such as some of the dependencies) in the various profiles.
http://maven.apache.org/pom.html#Profiles
Different dependencies for different build profiles in maven
answers a similar question - but you can swap in the "release" and "debug" for "Project A" and "Project C"
Provided dependencies are a difficult subject. First of all: Provided dependencies are not transitive in the following sense: If your project C has a provided dependency on X, then A will not get the dependency. It is silently ignored. This fits with the following meaning of "provided" which I propose:
Only the artifacts that are actually deployed should mark dependencies as "provided". Libraries or other jars that are not individually deployed to a specific server should not have provided dependencies. Instead, they should declare their dependencies as compile dependencies. In your example: Project C should have a compile dependency on X. If project A knows that X is provided, it sets X to provided in "dependencyManagement". As project A should know the environment in which it runs it should decide what is provided and what is not. And "dependenyManagement" is the right place to declare this.
If your project A should be able to run within and without a given server, you probably need to make a lot of adjustments, even change the type from ear to jar. So you either use build profiles for this, which then have different dependencyManagement entries, or you split A into two projects which depend on some other project that contains the common elements.
If some given project C already has a provided dependency on X and you cannot change that, this is effectively the same as a missing dependency in C. This has to be repaired at some point, and this could be project A itself.