How can I test if a jar is an OSGi jar? - osgi

Maybe it is a silly question, but what is the best way to test if a Java Archive (jar) file is an OSGi bundle? That is, what are the minimal requirements for the jar to be fully compatible? Is it the mere presence of a META-INF/MANIFEST.MF (I don't think so)? If not, what are the minimal fields that must be provided by this file?
Practically, how should I test if the jar is an OSGi jar?

Look for the Bundle-SymbolicName header in the MANIFEST.MF. This is the only mandatory header in an OSGi bundle, at least since Release 4.0 of the OSGi specification. Therefore if the Bundle-SymbolicName header is defined, then the JAR is an OSGi bundle. If not, then it is just a JAR.

A bundle is a group of Java classes and additional resources equipped with a detailed manifest MANIFEST.MF file on all its contents, as well as additional services needed to give the included group of Java classes more sophisticated behaviors, to the extent of deeming the entire aggregate a component.
The OSGi spec describes the bundle as a "unit of modularization" that "is comprised of Java classes and other resources which together can provide functions to end users.".
a bundle is a JAR file that:
Contains [...] resources
Contains a manifest file describing the contents of the JAR file and providing information about the bundle
Can contain optional documentation in the OSGI-OPT directory of the JAR file or one of its sub-directories
In short, a bundle = jar + OSGI information (specified in the JAR manifest file - META-INF/MANIFEST.MF), no extra files or predefined folder layout are required.
I guess the only qualification be required is that there are a bundle of classes and that it contains a MANIFEST.MF file which contains valid OSGi headers.
Consider this link
As well as this
Answering your question, the only way to test the bundle it to check if the MANIFEST.MF file exists and contains valid headers.

Related

Does sbt/maven includes the complete dependency in jar file?

I am building a rest API for a previous project. If I add it as dependency to my new rest API project, will all the functionality will be added to my rest API jar file or only the methods which I use? My old project jar is 181M, so the whole will be added to Rest API jar?
Thanks,
Without special configuration, Maven will not include any dependencies in your jar at all. Just the compiled classes from your source code (and generated classes) are included. If you want to include dependencies, you have to tell maven using the maven-assembly-plugin, as described in How can I create an executable JAR with dependencies using Maven?. When you use a different packaging, the dependencies can be included automatically, depending on the packaging type. E. g. when using war, all compile dependencies are added to WEB-INF/lib.
When you include dependencies, always the complete dependency-jar is added to your jar. There will be no checking, if everything is needed. So, your resulting jar will contain all 181M of dependencies as well as the source code of the new project.

Do I need to create seperate OSGi bundles for each jar

I want to create an OSGi bundle for log4j2. I need to use some additional jars along with log4j2 such as log4j2-jcl, log4j2-jul, log4j2-web etc. I want to clarify some confusions listed below about this.
However this is not specific to log4j2 I'm asking this as a general procedure.
Do I need to create separate OSGi bundles for each jar or can I use one bundle for all the jars? (What is the best practice)
How can I know whether OSGi bundles are already available for these jars ? (So that I don't need to re create bundles, can use existing ones)
I'm quite new to OSGi so can anyone please clarify these things no need to specific for log4j2.
Thanks!
Do I need to create separate OSGi bundles for each jar or can I use one bundle for all the jars? (What is the best practice)
I recommend to create one bundle per JAR, if possible. This gives you the greatest flexibility and makes sure that each JAR is properly designed.
How can I know whether OSGi bundles are already available for these jars ? (So that I don't need to re create bundles, can use
existing ones)
Just have a look at the Manifest file.
If it contains the OSGi Metadata -> it's a OSGi bundle.
If it doesn't contain the OSGi Metadata -> it's not a OSGi bundle.
I think the Log4J JARs already provide the OSGi Metadata (-> they are already bundles), but I haven't double-checked.
just a recommendation.
before you do any bundle, check if it exists.
https://jpm4j.org/#!/search?q=log4j2
also, I recommend slf4j

What is the use of converting jars to bundles in WAB(OSGI)?

After exploring the OSGi framework, I had developed a sample web application.The Web Application Bundle(.war or .jar) is packaged inside an eba.
The war file contains a bunch of embedded jar files in its WEB-INF/lib
directory.These jars have been converted to OSGi bundles(using maven-bundle-plugin) with the required export and import packages as per the relation between the jars.
Now I have to even mention all these jars(WEB-INF/lib) in the bundle-classpath.
The above works because a bundle(wab is also a bundle) can include one or more jarfiles within it, and use a Bundle-Classpath manifest.mf entry to point to them.
Incase I dont include the jars in the bundle-classpath I get a ClassNotFoundException.
The question is,then there's no point converting the jars to osgi bundles.Obviously all the jars in the WEB-INF/lib are loaded by the same class loader(i.e wab's class loader) ,so then we are not reaping the major benefits of OSGi which is mainly a per bundle classloader concept?
Putting jars inside the WEB-INF/lib is the old-style normal-java way of handling dependencies, and putting them outside the war is the new-style OSGi way of handling them.
By packaging your war's dependencies in WEB-INF/lib, you're treating them as normal jars (remember a bundle is also a jar). So in that case, you're right that there wasn't much point in using bundles.
One of the benefits of using wabs instead of wars is to get away from the dreaded 100 Mb monolithic war. Rather than packaging the bundles inside WEB-INF/lib, try having the war import the packages it needs using Import-Package:, and package the dependencies inside the eba. (If you don't remember to have the war import the packages it needs, you'll get the class not found exceptions you were seeing, because the OSGi container won't know your war needs those packages.)

cxf webservice in standalone spring application packaged as jar-with-dependencies using maven

When running the packaged app like "java -jar my-app-0.0.1-SNAPSHOT-jar-with-dependencies.jar", I get the following error:
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 47 in
XML document from URL [jar:file:/.../cxf/javafirst/target/my-app-0.0.1-SNAPSHOT-jar-with-dependencies.jar!/application-context.xml]
is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 47;
columnNumber: 61; cvc-complex-type.3.2.2: Attribute 'sendServerVersion'
is not allowed to appear in element 'httpj:engine'.
This is due to an outdated http-jetty.xsd schema published at apache.org. And that is NOT my problem. My problem is that this does not happen in cases like:
maven jetty:run
or
maven exec:java
where the service runs directly against the unpacked binary directories and obviously finds the updated and correct http-jetty.xsd schema file.
I want to do basically one thing:
put the webservice into one jar including dependencies. That should also include all XML schema files because, obviously, it is not a good idea to make a web service dependent on outside resources.
What's the best way to do that using maven?
My possibly naive solution would be to copy the XML schema files manually into the resources directory and tell CXF to resolve them in the jar file. Therefore I have two more specific questions:
1.) Is it possible to let maven find the XML schema files and copy them into appropriate places?
2.) What's the recommended way to make CXF look up the schema files in the jar file?
3.) Is there any better, best-practice solution to that problem?
My maven configuration regarding the maven assembly plugin is the direct combination of the last two sections at http://maven.apache.org/plugins/maven-assembly-plugin/usage.html.
Spring has a very good mechanism to resolve the schema files that it requires - it typically does not download it from the web at all, instead using locally available files within jar files to get the schema and validate the xml, for eg. consider the context custom namespace schema in Spring, if you look at the META-INF/spring.schemas file in spring-context.jar file, you will see an entry along these lines:
http\://www.springframework.org/schema/context/spring-context-3.1.xsd=org/springframework/context/config/spring-context-3.1.xsd
basically what it is saying is to resolve the spring-context-3.1.xsd files from the classpath org.springframework.context.config.spring-context-3.1.xsd file
This applies for any third party library also, which in your case is http-jetty.xsd.
I think what I would recommend is to simply create a spring.schemas file in your jar file in META-INF/ folder, put an entry for the full path to the schema and replace it with a classpath version of http-jetty.xsd.

Classloader issue

I've got an interesting question about classloader behavior.
Question one: What is the order in which the classloader will load jars?
The following jars and containing classes are given:
a.jar
+-com/scheffield/foo/A.class
b.jar
+-com/scheffield/foo/B.class
Which class will be loaded?
Question two: Is it true that the path and name of a file in the classpath is unique?
The following jars and containing classes are given (realworld example):
spring-beans-3.0.3.RELEASE.jar
+-META-INF/spring.schemas
spring-aop-3.0.3.RELEASE.jar
+-META-INF/spring.schemas
What I can tell you is that both files are be loaded by Spring otherwise an exception would occur (see this article).
Why am I asking that:
I made a so called big jar (cookbook entry for gradle). Thats a jar with the application classes and all other dependencies unzipped and packet in the big jar. And I'm not absolutely sure what to do with duplicated files.
Classes are resolved however a classloader wants to resolve them (that's the whole point of having a classloader architecture). Most classloaders you deal with in practice are variants of java.net.URLClassLoader which loads classes (and resources) based on a search path (class path) of directories and jars. Each location in the search path is treated as a source of classes and the locations are searched in order.
No, names are not unique. The first one encountered in the search order will be used.
If you combine jars into one big jar there is a definite possibility of conflict. If you are careful to merge them from the last source in your effective classpath to the first (thus overriding later jars with earlier jars), you will get approximately the same result.
I say approximately because the manifests in jars contain additional processing instructions that need to be merged as well. For example, a manifest can contain a Class-Path attribute that includes additional jars in the classpath. It's possible to merge jars but lose manifest attributes that are specifying part of your actual necessary classpath. If your manifest contains sealed or signed jars then you might not be able to do this merging at all without violating the signed parts of the jar.
In summary, jars are not really designed to be merged in this way. It can work but there are many possibilities for error, some of which are not possible to solve. One common cause of error is to merge two jar files and end up with more than one entry with the same path, which is allowed in zip files. The ant jar and zip tasks allow you to merge multiple sources and can produce these kinds of issues.
Really, it's better to instead bundle web apps consisting of many jars and sources into a single WAR or EAR archive. That's kind of the whole point of why they exist.
the files are being loaded in the order the containing jars appear on the classpath. this applies to classes. if you are loading other resources (like the spring.schema) you may use either classloader.getResource(...) or classloader.getResources(...). the first one returnes the first resource on the classpath, the second one also returnes shadowed resources.
I don't think a valid zip archive contains duplicate entries.
regards

Resources