AEM OSGI 3rd party dependency, bundle not Active - java

I'm working off an AEM project, I'm new to AEM btw. I'm trying to use a couple of 3rd party non-OSGI libraries, like Dozer and Spring. I'm really struggling with this, it is a pain in the ass, maybe it is because OSGI is kind of old school. My bundles get installed, but not Active in the OSGI console, as they complain about missing packages.
I want to do step by step. First I want to make work my bundles with just Dozer and nothing else. This is my pom dependencies:
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer-osgi</artifactId>
<version>5.5.1</version>
</dependency>
Here is my maven-bundle-plugin configuration:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>4.1.0</version>
<configuration>
<instructions>
<Export-Package>somepackages*</Export-Package>
<Private-Package/>
<Import-Package>*</Import-Package>
<Sling-Bundle-Resources>/META-INF</Sling-Bundle-Resources>
</instructions>
</configuration>
</plugin>
As you can see, I'm using dozer-osgi already. I'm not sure why it doesn't pick it up. It complains about this:
org.dozer,version=[5.5,6) -- Cannot be resolved
org.dozer.loader.api,version=[5.5,6) -- Cannot be resolved
First, I don't understand why it says [5.5,6), because I'm telling it to use 5.5.1. Second, I'm using dozer-osgi already, I believe it should pick it up automatically.
I tried also using:
<Embed-Dependency>dozer-osgi</Embed-Dependency>
Whit dozer-osgi added, things start to get better, and more complex at the same time. It seems like now it loads dozer, but starts complaining about transitive dependencies:
android.dalvik -- Cannot be resolved
dalvik.system -- Cannot be resolved
javassist.util.proxy -- Cannot be resolved
org.apache.commons.beanutils,version=[1.9,2) -- Cannot be resolved
org.apache.commons.beanutils.converters,version=[1.9,2) -- Cannot be resolved
The beanutils utils is easy to fix. I just need to add the regarding dependencies like this:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
and add "commons-beanutils" here:
<Embed-Dependency>dozer-osgi,commons-beanutils</Embed-Dependency>
Now, it complains about:
android.dalvik -- Cannot be resolved
dalvik.system -- Cannot be resolved
javassist.util.proxy -- Cannot be resolved
org.hibernate.proxy -- Cannot be resolved
The hibernate dependency error is new. I thought beanutils depended on hibernate, but not, is Dozer the one depending on it.
I tried adding hibernate, like this:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.9.Final</version>
</dependency>
<Embed-Dependency>dozer-osgi,commons-beanutils,hibernate-core</Embed-Dependency>
And so on, it is like a look, it gets worse and worse everytime I try to add a transitive dependency. I also tried this, only keeping dozer-osgi in my dependencies:
<Embed-Dependency>dozer-osgi</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
Result:
android.dalvik -- Cannot be resolved
dalvik.system -- Cannot be resolved
javassist.util.proxy -- Cannot be resolved
org.hibernate.proxy -- Cannot be resolved
org.apache.commons.beanutils -- Cannot be resolved
org.apache.commons.beanutils.converters -- Cannot be resolved
It is like if Embed-Transitive doesn't work at all.
What is the best way to work with 3rd party libraries when using Maven and AEM? In my case, Maven is using to install the bundles in my AEM instance.

In OSGi you have to distinguish between the build and the runtime. At build time your initial approach was totally fine.
You use dozer and the maven-bundle-plugin creates suitable Import-Package statements. You can check these by looking into the Manifest of the jar.
The rules for package imports result in a range of version that should work with your code. So [5.5,6) is exactly the expected import range.
Now to runtime. Here you have to supply all dependencies of your code (including the transitive ones) as bundles in AEM (or more generally in the OSGi runtime).
So you also need to install dozer OSGi as bundle. If this complains again then you also need to install its dependencies.
This is the default approach and normally totally fine.
Now if you want to make your bundle standalone in respect to having no additional runtime depenencies then you can try to embed all dependencies. Then you only need to install your bundle.
Be aware though that this is not easy. If your code uses some of the embedded classes in its own API then you get into lots of problems with embedding. So if your are not very experienced then better go the way of installing all dependencies as bundles.
To simplify the install process you can create a content package that contains all needed bundles.
You also have to check that all dependencies actually are bundles. In some cases the normal maven dependencies are not suitable. In this case have a look at apache servicemix bundles. It is a project that creates OSGi bundles for popular dependencies and deploys these to maven central.

Related

How to ignore a dependency in dependency-lock-maven-plugin

In my maven project I want to implement dependency-locking.
One approach I've found is using dependency-lock-maven-plugin.
My project has multiple maven modules.
parent
core
service
third-maven-module
When I run mvn clean package it will generate a new core-0.0.1-SNAPSHOT.jar every time.
Now apart from checking version dependency-lock-maven-plugin also checks SHA, which in this case gets changed every time & then the plugin throws error stating SHA is different.
Now to avoid this in plugin's version 0.0.78f56707b3a1d639c8e769bba1686587e9a8956 we can simple add below lines:
<configuration>
<ignore>
<dependency>com.myservice:core:*</dependency>
</ignore>
</configuration>
It works but now I can see that this version has vulnerabilities, so I want to use the latest version 1.0
But it looks like the configuration to ignore a dependency has been changed & documentation does not says much.
Please suggest how can I ignore a dependency in dependency-lock-maven-plugin.

Resolving conflict between transitive dependencies

I have a pom.xml where i've got hadoop-core dependency as provided
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-core</artifactId>
<version>${hadoop.version}</version>
<scope>provided</scope>
</dependency>
When I add cfg4j as compile time dependency
<dependency>
<groupId>org.cfg4j</groupId>
<artifactId>cfg4j-core</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.cfg4j</groupId>
<artifactId>cfg4j-consul</artifactId>
<version>4.4.0</version>
</dependency>
I've got an exception "java.lang.NoSuchMethodError: javax.ws.rs.core.Response.someMethod". I've investigated the problem and find out that the problem is from hadoop and cfg4j-consul. Hadoop core depends to jersey-core and cfg4j depends on cxf. Both declared javax.ws.rs as dependecy so the problem is that jersey has version 1.1 and cxf has 2.0.2. Hadoop dependency is provided, cause it's needed by Flink (framework) and it's in the lib folder. I can't just upgrade it or remove it, nor add it as compile time and exclude the lib. Even I was able to do it, I have no guarantees that hadoop will work as expected. I guess shading doesn't fix the problem cause it's not with cfg4j but one of the dependency of his dependency.
Is there way to resolve the conflict? Does gradle has it's onw ways to fix such issue?
Two approaches:
Shading: A bit more difficult as you say because this a transitive dependency, but I would have a look a Maven shade plugin and it would still be possible to declare the dependency directly if necessary.
Don't use the dependency and try to find some other library or solution for your problem.
Try the following steps, here is the source: https://reflectoring.io/nosuchmethod/
Your issue has nothing to do with the choice between Mavern and Gradle, switching therefor will not help.
Fixing a NoSuchMethodError
There are a lot of different flavors of NoSuchMethodErrors, but they all boil down to the fact that the compile time classpath differs from the runtime classpath.
The following steps will help to pinpoint the problem:
Step 1: Find Out Where the Class Comes From
First, we need to find out where the class containing the method in question comes from. We find this information in the error message of the NoSuchMethodError:
Exception in thread "main" java.lang.NoSuchMethodError:
io.reflectoring.nosuchmethod.Service.sayHello(Ljava/lang/String;)Ljava/lang/String;
Now, we can search the web or within the IDE to find out which JAR file contains this class. In the case above, we can see that it’s the Service class from our own codebase and not a class from another library.
If we have trouble finding the JAR file of the class, we can add the Java option -verbose:class when running our application. This will cause Java to print out all classes and the JARs they have been loaded from:
[Loaded io.reflectoring.nosuchmethod.Service from file:
/C:/daten/workspaces/code-examples2/patterns/build/libs/java-1.0.jar]
Step 2: Find Out Who Calls the Class
Next, we want find out where the method is being called. This information is available in the first element of the stack trace:
Exception in thread "main" java.lang.NoSuchMethodError:
io.reflectoring.nosuchmethod.Service.sayHello(Ljava/lang/String;)Ljava/lang/String;
at io.reflectoring.nosuchmethod.ProvokeNoSuchMethodError.main(ProvokeNoSuchMethodError.java:7)
Here, the class ProvokeNoSuchMethodError tries to call a method that does not exist at runtime. We should now find out which library this file belongs to.
Step 3: Check the Versions
Now that we know where the NoSuchMethodError is provoked and what method is missing, we can act.
We should now list all of our project dependencies.
In Gradle, we can call:
./gradlew dependencies > dependencies.txt
If we’re using Maven, a similiar result can be achieved with:
mvn dependency:list > dependencies.txt`
In this file, we can search for the libraries that contain the class with the missing method and the class that tries to call this method.
Usually we’ll find an output like this somewhere:
\--- org.springframework.retry:spring-retry:1.2.2.RELEASE
| \--- org.springframework:spring-core:4.3.13.RELEASE -> 5.0.8.RELEASE
The above means that the spring-retry library depends on spring-core in version 4.3.13, but some other library also depends on spring-core in version 5.0.8 and overrules the dependency version.
We can now search our dependencies.txt file for 5.0.8.RELEASE to find out which library introduces the dependency to this version.
Finally, we need to decide which of the two versions we actually need to satisfy both dependencies. Usually, this is the newer version since most frameworks are backwards compatible to some point. However, it can be the other way around or we might even not be able to resolve the conflict at all.

Upgrading gwt from 2.1.1 to 2.8.0: ServiceConfigurationError: org.apache.juli.logging.Log: Provider org.eclipse.jetty.apache.jsp.JuliLog not a subtype

After upgrading gwt from version 2.1.1 to 2.8.0, I got the error message
2017-04-20 12:59:19.551:WARN:oejuc.AbstractLifeCycle:main: FAILED c.g.g.d.s.j.WebAppContextWithReload#341fbaf1{/,file:/C:/Users/xxx/.IntelliJIdea2017.1/system/gwt/xxx.97baa614/xxx.fdf824a8/run/www/,STARTING}{C:\Users\xxx\.IntelliJIdea2017.1\system\gwt\xxx.97baa614\xx.fdf824a8\run\www}: java.util.ServiceConfigurationError: org.apache.juli.logging.Log: Provider org.eclipse.jetty.apache.jsp.JuliLog not a subtype
java.util.ServiceConfigurationError: org.apache.juli.logging.Log: Provider org.eclipse.jetty.apache.jsp.JuliLog not a subtype
I found some other posts with similar messages, like this or this, but the situation seems to be different:
I do not use Maven or Ant, just pure IntelliJ, I have no reference to any Tomcat library, and I am not aware of any JSP in our application.
I found through debugging that first the class loader com.google.gwt.dev.shell.jetty.Jettylauncher$WebAppContextWithReload$WebAppClassLoaderExtension loads class org.eclipse.jetty.apache.jsp.JuliLog including interface org.apache.juli.logging.Log.
Then, later interface org.apache.juli.logging.Log is loaded by sun.misc.Launcher$AppClassLoader triggered indirectly by
org.eclipse.jetty.webapp.WebAppContext.startContext()
which calls method initialize of an
org.eclipse.jetty.jsp.JettyJspServlet
instance.
I have no idea why a JspServlet needs to be initialized at all, as no JSPs are used in the application, as far as I see, just a few Servlets. And it seems all the classes involved in this conflict are contained in the single jar gwt-dev.jar, so I see no possibility to influence any class loading behavior via class path settings.
Any idea how I could resolve this?
I also got this error upgrading from gwt from version 2.4 to 2.8.2.
Jake W's answer helped me.
To solve this, I ran a maven dependency tree on my project to figure out what was referencing jetty's apache-jsp.
To run the dependency tree, in Eclipse I created a new run configuration -> maven build -> with the goals "dependency:tree -Doutput=/dependency/file.txt". Once it's run, the console output will show where it saves the output. It should be the same location that you referenced with the -Doutput option.
Look for something like this in the output file:
- org.eclipse.jetty:apache-jsp:jar:9.2.14.v20151106:compile
And then look up in the tree to see where it's being pulled in from. In my case it came from this:
+- com.google.gwt:gwt-dev:jar:2.8.2:compile
+- net.sourceforge.htmlunit:htmlunit:jar:2.19:compile
\- org.eclipse.jetty:apache-jsp:jar:9.2.14.v20151106:compile
Once you know where it's coming from, (assuming you're using maven) you can add an exclusion in your pom.xml file for it:
</dependencies>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
<version>${gwt.version}</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
This worked for me. Thanks :)
I saw this error when I recently upgraded to GWT 2.8.0. Please try to exclude jetty-apache-Jsp related dependencies from your project.
You may see other jetty related issues as well, so please also make sure you are using exactly the same jetty version as GWT 2.8.0 is using.
I'm on mobile at the moment, unable to add more details, but I hope that can be a useful direction to go. Please add your comments if you still see issues, I will then have a look and update the answer when I'm on my laptop.
I have just ran into this exception after adding gwt-test-utils:0.53 dependency (with GWT 2.8.1)
I am using ant and all information found regarding this error indicated there was 2 versions of Juli Logging in the classpath, but every search came up with only gwt-dev.jar. Production builds worked fine, but dev mode did not which needs gwt-dev.jar.
Part of the build process has the jars copied from a local lib directory to war/WEB-INF/lib to pack into the war. The ant script points to the local lib directory for debugging, not the ones meant for the war file. Despite the war location not being listed in the ant file as a class path, it was still loading it.
Ultimately, gwt-dev.jar was conflicting with the copied version of itself.

Maven: Resolving Duplicate Dependencies

I'm developing an application that will be used internally at our company. In order for it to interop with our other internal systems I have to use some maven dependencies that we use internally, but this is causing some issues with using some external 3rd party dependencies that I also need.
So essentially my pom looks like this:
<dependencies>
<dependency>
internal-framework-artifact
</dependency>
<dependency>
necessary-third-party-artifact
</dependency>
</dependencies>
I've come to find that both of these dependencies have the apache's commons-collections as one of their own dependencies (among a large number of others, but we'll just keep it at one for this question's simplicity).
If I place exclusion rules on both of them for the commons-collections pom I can compile the project, but my resulting jar won't have access to either version of commons-collections and will just result in a java.lang.NoClassDefFoundError exception. Removing the exclusion rule on either of them just results in a mvn compiler error:
[WARNING] Rule 2: org.apache.maven.plugins.enforcer.BanDuplicateClasses failed with message:
Duplicate classes found:
I've been looking through various so q/a's and I can't really seem to find something that's 100% relevant to my situation. I'm really at a loss as to how to resolve this. Am I missing something really obvious?
I've never actually used the maven-shade-plugin for shading, but I think this is the exact use case it was designed for.
Create a new project that uses the maven-shade-plugin (see: http://maven.apache.org/plugins/maven-shade-plugin/) to produce an uber-jar version of internal-framework-artifact which contains that classes in internal-framework-artifact and all its dependencies. Configure the plugin so that it relocates all the classes that are also dependencies of necessary-third-party-artifact to some non-conflicting package names. This new project should produce a .jar with a different name, something like internal-framework-artifact-with-dependencies.
Now modify your original pom so that it is dependent on internal-framework-artifact-with-dependencies instead, and it should work.

Is it possible to include only the used classes in a war file with maven build?

Say I have this dependency in my pom.xml file:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
</dependency>
When I do a
clean install
all the javaee-api-6.0.jar will be included in the war file under WEB-INF\lib folder.
Is it possible that instead of including the whole jar, only classes that I use and their dependencies are included?
If you're deploying into a Java EE application server, that entire JAR is already provided by the application server, and can be omitted from the WAR file. You can accomplish this by putting it into the provided scope:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
That makes that dependency available for compilation and test execution, but will not package it into the WAR.
In contrast, trying to determine which individual classes you need so you can only include their class files is an ultimately pointless endeveor. The JVM only loads classes when they are used - that is, unused classes are not loaded.
It is generally impossible to identify the used classes at compile time due to reflection. For instance, consider:
System.console().printf("Please specify the implementation class to use");
String className = System.console().readLine();
FooService service = (FooService) Class.forName(className).newInstance();
service.work();
You can get the JVM to log which classes are loaded, but different executions can use different classes ...
It's not a viable option - at least not in maven, although You know which classes You are using, but You don't know what are the dependencies for each class that You imported - so it might be impossible satisfy it's requirements. This is why we are using tools like maven - to ease the process importing a library.
Read some more about reduce size of built project and see what are Your options there
Except for UberJAR, Your biggest chance (IMHO) would be to identify libraries that are provided by the container, and use provided scope for them.
You also could integrate 3rd party tools like ProGuard
You could use exclusions.
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
</dependency>
But I don't think you could exclude at class-levels. This only excludes dependencies useful when there are conflicting dependencies in your project.
It is really not a viable option in my opinion ,as its almost impossible to know internals what all classes are required at runtime until and unless you are seeing the,implementation of all the,3rd part apis that you are using.
I also think the whole idea behind the maven is to ease the development and build process so that you won't have to do any effort in identifying the artifacts that are required at runtime or compile time. Maven will automatically figure out that for you.

Categories

Resources