I am trying to integrate Adobe AEM 6.3 (running on Java 1.8) with Cloudinary SDK. I have done the following but, keep hitting an exception that I am not able to resolve. Has anyone integrated Cloudinary with AEM and run into similar issues?
pom.xml
for compiling the code. <dependency>
<groupId>com.cloudinary</groupId>
<artifactId>cloudinary-core</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>com.cloudinary</groupId>
<artifactId>cloudinary-http44</artifactId>
<version>1.24.0</version>
</dependency>
Build an OSGI plugin to ensure AEM gets the right jar files. For this purpose, I followed the steps to create a third party RESTful service example. To build the bundle, I had to explicitly download the following jar files: cloudinary-1.0.14.jar, cloudinary-core-1.21.0.jar, cloudinary-http44-1.21.0.jar, commons-codec-1.10.jar, commons-collections-3.2.2.jar, commons-lang3-3.1.jar, commons-logging-1.2.jar, httpclient-4.4.jar, httpmime-4.4.jar, jsp-api-2.0.jar
Despite creating a bundle that has httpclient
, I get the following exception when trying to upload an image to Cloudinary. Here's code and the exception.
Code snippet
import com.cloudinary.*;
..
Cloudinary cloudinary = new Cloudinary("<<credentials>>");
...
File toUpload = new File("/Users/akshayranganath/Downloads/background-2633962_1280.jpg");
try {
Map uploadResult = cloudinary.uploader().upload(toUpload, ObjectUtils.emptyMap());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Exception
Caused by: java.lang.NoClassDefFoundError: javax/net/ssl/HostnameVerifier
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.defineClass(BundleWiringImpl.java:2370)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.findClass(BundleWiringImpl.java:2154)
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1542)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:79)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:2018)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at org.apache.http.impl.conn.SchemeRegistryFactory.createDefault(SchemeRegistryFactory.java:52)
at org.apache.http.impl.client.AbstractHttpClient.createClientConnectionManager(AbstractHttpClient.java:321)
at org.apache.http.impl.client.AbstractHttpClient.getConnectionManager(AbstractHttpClient.java:484)
at org.apache.http.impl.client.AbstractHttpClient.createHttpContext(AbstractHttpClient.java:301)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:818)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
at com.cloudinary.Uploader.callApi(Uploader.java:317)
at com.cloudinary.Uploader.upload(Uploader.java:57)
at com.aem.community.core.models.HelloWorldModel.init(HelloWorldModel.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.sling.models.impl.ModelAdapterFactory.invokePostConstruct(ModelAdapterFactory.java:792)
at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:607)
... 211 common frames omitted
Caused by: java.lang.ClassNotFoundException: javax.net.ssl.HostnameVerifier not found by MyBundle [550]
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1574)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:79)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:2018)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 236 common frames omitted
This is the first time I am working with AEM and I may not be following the right steps. Please let me know if anyone has been able to get past this issue.
Based on Alexander's suggestion and a pointer from another source, I added the following code to the parent pom.xml
file.
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<instructions>
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
<Embed-Directory>OSGI-INF/lib</Embed-Directory>
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
After making this change, the cloudinary libraries were being added to the bundle. Here's the output from AEM: http://localhost:4502/system/console/bundles
Embedded-Artifacts: OSGI-INF/lib/cloudinary-http44-1.21.0.jar; g="com.cloudinary"; a="cloudinary-http44"; v="1.21.0", OSGI-INF/lib/commons-lang3-3.1.jar; g="org.apache.commons"; a="commons-lang3"; v="3.1", OSGI-INF/lib/httpclient-4.4.jar; g="org.apache.httpcomponents"; a="httpclient"; v="4.4", OSGI-INF/lib/httpcore-4.4.jar; g="org.apache.httpcomponents"; a="httpcore"; v="4.4", OSGI-INF/lib/commons-logging-1.2.jar; g="commons-logging"; a="commons-logging"; v="1.2", OSGI-INF/lib/commons-codec-1.9.jar; g="commons-codec"; a="commons-codec"; v="1.9", OSGI-INF/lib/httpmime-4.4.jar; g="org.apache.httpcomponents"; a="httpmime"; v="4.4", OSGI-INF/lib/cloudinary-core-1.21.0.jar; g="com.cloudinary"; a="cloudinary-core"; v="1.21.0"
However, I now get an error with this message:
org.apache.avalon.framework.logger -- Cannot be resolved
org.apache.log -- Cannot be resolved
I am able to resolve the org.apache.avalon.framework.logger
error by adding a dependency Avalon framework. But, I am not able to get over the org.apache.log
issue. It looks like there is a version conflict that is causing the problem.
This new error starts when I include the Cloudinary http44 library. This library doesn't appear to directly reference logging (see here for dependencies). Due to this error, the application still fails to go from Installed to Active state.
Cloudinary-libs are available as Maven artifacts. Such JAR-files can be put in your bundle as private libraries with the maven-bundle-plugin.
The following sample works for me (even with Cloudinary test account)
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<!-- Create the bundle late in the compile-phase instead of the package-phase.
So the generated OSGi meta-data is available during JUnit tests. -->
<id>run-before-tests</id>
<phase>process-classes</phase>
<goals>
<goal>bundle</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-Name>Test Bundle</Bundle-Name>
<Embed-Dependency>*;groupId=com.cloudinary;scope=compile|runtime</Embed-Dependency>
<Embed-Directory>OSGI-INF/lib</Embed-Directory> <!-- not needed, but nice -->
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
...
<dependencies>
<dependency>
<groupId>com.cloudinary</groupId>
<artifactId>cloudinary-core</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>com.cloudinary</groupId>
<artifactId>cloudinary-http44</artifactId>
<version>1.24.0</version>
</dependency>
...
In general embedding an external library can be from simple, cumbersome to impossible. It depends on the dependencies of the imported artifacts.
Check the dependency tree manually! (e.g. https://mvnrepository.com/)
You have to fiddle with 3 instructions:
Embed-Dependency
This are the libraries, that are put in your bundle. Be careful with the asterisk operator, otherwise you may include way too many dependencies (in case of AEM easily half of the internet). But do not include too less! Extract the built bundle.jar, to see what is actually included (in case of cloudinary it was easy).
Import-Package
Often the libs have way too many dependencies, especially if libs come an other ecosystem (like Spring or JEE containers), or have a lot of semi-optional dependencies. With this setting you can tell OSGi, that a bundle can be activated, even if certain dependencies are not available.
This is a real world example :
<Import-Package>
!com.sun.msv.*,
!org.apache.log4j.jmx.*,
!sun.misc.*,
!org.jboss.logging.*,
!org.apache.zookeeper.*,
*
</Import-Package>
Export-Package
Normally the library should be bundle-private. But sometimes you have to import differently, or the lib does something automatically. So you should always check in the system console, what your bundle is exporting. In case it is not right, you have to manually fiddle with this setting:
Here is an example:
<Export-Package>
!*.internal,
!*.internal.*,
!*.impl,
!*.impl.*,
com.mycompany.myproject.mybundle.*
</Export-Package>
By default all packages *
are exported, except they are named impl
or internal
. Also their child packages are private (the !*.impl.*
rule). If the default doesn't work, then export with this instruction only what you need.
Whatever you export goes to the global OSGi space. As also the AEM- and Sling-Bundles are not perfect nor 100%-bugfree, please make sure
If you don't ensure this, you might experience strange deployment issues - that are very difficult to find/solve.
So the best is, NOT to export anything that is imported by any AEM out-of-the-box bundle. Everything else is for Experts-only. And even they overestimate themselves, and underestimate the long-term costs of patching AEM manually.
PS: the _removeheaders
instruction could remove all osgi-instructions that are not needed for runtime. But only do this, if you want to provide a bundle to the public and make it totally shiny. I would leave it in, as it is some kind of documentation.