Tuesday, 3 June 2014

Adobe CQ5 Integration With Apache CXF

During project development, I got a task in which I have to integrate CQ 5.6.1 with Apache CXF. In this post I'll discuss first approach for this integration. This post is based on the discussion in adaptTo() conference  2011.

Brief Introduction to Apache CXF 
"Apache CXF is an open source services framework. It uses JAX-WS and JAX-RS APIs so that it can handle a variety of protocols such as SOAP, XML/HTTP, RESTful HTTP, or CORBA and work over a variety of transports such as HTTP, JMS or JBI."

Platform used
  1. JDK 1.6 or higher version.
  2. Maven 3.0.4.
  3. Adobe CQ 5.6.
Goals of this Post

  1. Adobe CQ integration with Apache CXF.
  2. Java proxy classes generation from a wsdl file Using Apache CXF in CQ project.
  3. Use these proxy classes in Adobe CQ5.
I have created a maven project with two sub-modules one is bundle & other is content so that there are
three pom.xml files one for each i.e. parent, bundle, content.

My project structure looks like -


 First I will create proxy classes based on wsdl file using    Apache CXF. For this post, wsdl url is -
 http://www.webservicex.net/ConvertTemperature.asmx

 It is a temperature converter service.

 In this service, I provide temperature in Celcius & it  convert it into Fahrenheit.

 When you Open this link you will see a link "service  Description". 

 Click on It, you will get a wsdl related to this service.

 Save this wsdl file with a name
 ConvertTemperature.wsdl.

 Go to your project and create a folder named as wsdl as  shown in fig. & place ConvertTemperature.wsdl file in  this folder.


Now copy & paste these dependencies in to your projects parent pom.xml file.

<!-- Apache CXF Dependencies -->
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-bundle-minimal</artifactId>
                <version>2.7.11</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-jms</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aop</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-beans</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-core</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-context</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-expression</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-asm</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-tx</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-server</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-continuation</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-http</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-io</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-util</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-security</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-api</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-xjc</artifactId>
                <version>2.2.6</version>
                <scope>compile</scope>
            </dependency>

In parent pom.xml file and search for maven-bundle-plugin & replace this plugin definition with given configuration

            <plugin>
                    <groupId>org.apache.felix</groupId>
                    <artifactId>maven-bundle-plugin</artifactId>
                    <version>2.3.7</version>
                    <extensions>true</extensions>
                    <configuration>
                        <instructions>
                            <Export-Package>
                                com.ig.integration;version=${project.version},
                                com.ig.util.*;version=${project.version},
                            </Export-Package>
                            <Include-Resource>{maven-resources}</Include-Resource>
                            <Private-Package>
                                javax.wsdl;-split-package:=merge-first,
                                org.apache.cxf;-split-package:=merge-first,
                                org.apache.cxf.*;-split-package:=merge-first,
                                javax.xml.*;-split-package:=merge-first,
                                org.apache.ws.commons.schema.resolver.*;-split-package:=merge-first,
                                org.apache.ws.commons.schema.extensions.*;-split-package:=merge-first,
                                org.apache.ws.commons.schema.*;-split-package:=merge-first,
                                com.ig.integration.impl.*,
                                net.webservicex,
                            </Private-Package>
                            <Include-Resource>{maven-resources}</Include-Resource>
                            <Embed-Dependency>*;scope=compile|runtime;inline=false</Embed-Dependency>
                            <Embed-Transitive>true</Embed-Transitive>
                            <DynamicImport-Package>
                                com.sun.javadoc,
                                com.sun.tools.javadoc,
                                org.eclipse.jetty,
                                com.wordnik.swagger.jaxrs.config,
                                com.wordnik.swagger.jaxrs.listing,
                                com.sun.mirror.type,
                                com.sun.msv.*,
                                com.sun.source.tree,
                                com.sun.source.util,
                                com.sun.xml.fastinfoset.sax,
                                com.sun.xml.fastinfoset.stax,
                                javax.persistence,
                                javax.persistence.criteria,
                                javax.persistence.metamodel,
                                javax.resource.spi.endpoint,
                                jp.co.swiftinc.relax.schema,
                                jp.co.swiftinc.relax.verifier,
                                junit.framework,
                                net.jcip.annotations,
                                net.sf.cglib.proxy,
                                org.apache.aries.blueprint,
                                org.apache.aries.blueprint.mutable,
                                org.apache.avalon.framework.logger,
                                org.apache.axiom.om,
                                org.apache.commons.ssl,
                                org.apache.crimson.jaxp,
                                org.apache.cxf.tools.common,
                                org.apache.cxf.tools.common.model,
                                org.apache.cxf.tools.util,
                                org.apache.cxf.tools.validator,
                                org.apache.cxf.tools.wsdlto.core,
                                org.apache.cxf.ws.mex,
                                org.apache.cxf.ws.mex.model._2004_09,
                                org.apache.geronimo.osgi.registry.api,
                                org.apache.log4j.spi,
                                org.apache.lucene.document,
                                org.apache.lucene.index,
                                org.apache.lucene.search,
                                org.apache.mina.core.buffer,
                                org.apache.mina.core.filterchain,
                                org.apache.mina.core.future,
                                org.apache.mina.core.service,
                                org.apache.mina.core.session,
                                org.apache.mina.filter.codec,
                                org.apache.mina.filter.logging,
                                org.apache.mina.transport.socket.nio,
                                org.apache.tools.ant,
                                org.apache.tools.ant.taskdefs,
                                org.apache.tools.ant.taskdefs.compilers,
                                org.apache.tools.ant.types,
                                org.apache.velocity,
                                org.apache.velocity.app,
                                org.apache.velocity.context,
                                org.apache.xerces.impl.xpath.regex,
                                org.apache.xerces.parsers,
                                org.apache.xml.dtm,
                                org.apache.xml.utils,
                                org.apache.xpath,
                                org.apache.xpath.compiler,
                                org.apache.xpath.functions,
                                org.apache.xpath.objects,
                                org.bouncycastle.asn1,
                                org.bouncycastle.asn1.x509,
                                org.bouncycastle.util,
                                org.bouncycastle.util.encoders,
                                org.bouncycastle.x509.extension,
                                org.codehaus.jettison,
                                org.codehaus.jettison.badgerfish,
                                org.codehaus.jettison.json,
                                org.codehaus.jettison.mapped,
                                org.codehaus.jettison.util,
                                org.dom4j,
                                org.dom4j.io,
                                org.eclipse.jetty.continuation,
                                org.eclipse.jetty.http,
                                org.eclipse.jetty.io,
                                org.eclipse.jetty.security,
                                org.eclipse.jetty.server,
                                org.eclipse.jetty.server.handler,
                                org.eclipse.jetty.server.nio,
                                org.eclipse.jetty.server.session,
                                org.eclipse.jetty.server.ssl,
                                org.eclipse.jetty.util.component,
                                org.eclipse.jetty.util.thread,
                                org.hibernate,
                                org.hibernate.cache,
                                org.hibernate.cache.access,
                                org.hibernate.cfg,
                                org.hibernate.impl,
                                org.hibernate.stat,
                                org.hibernate.transaction,
                                org.jdom,
                                org.joda.convert,
                                org.junit,
                                org.jvnet.fastinfoset,
                                org.jvnet.staxex,
                                org.springframework.aop,
                                org.springframework.aop.framework,
                                org.springframework.aop.support,
                                org.springframework.beans,
                                org.springframework.beans.factory,
                                org.springframework.beans.factory.annotation,
                                org.springframework.beans.factory.config,
                                org.springframework.beans.factory.support,
                                org.springframework.beans.factory.wiring,
                                org.springframework.beans.factory.xml,
                                org.springframework.context,
                                org.springframework.context.annotation,
                                org.springframework.context.event,
                                org.springframework.context.support,
                                org.springframework.core,
                                org.springframework.core.io,
                                org.springframework.core.io.support,
                                org.springframework.core.task,
                                org.springframework.jms,
                                org.springframework.jms.connection,
                                org.springframework.jms.core,
                                org.springframework.jms.listener,
                                org.springframework.jms.support,
                                org.springframework.jms.support.converter,
                                org.springframework.jms.support.destination,
                                org.springframework.jndi,
                                org.springframework.transaction,
                                org.springframework.transaction.support,
                                org.springframework.util,
                                org.springframework.web.context,
                                org.springframework.web.context.support,
                                org.springframework.web.servlet,
                                org.springframework.web.servlet.handler,
                                org.springframework.web.servlet.mvc,
                                org.apache.log,
                                org.osgi.service.blueprint.container,
                                org.osgi.service.blueprint.reflect,
                                org.owasp.esapi,
                                sun.misc,
                                sun.nio.cs
                            </DynamicImport-Package>
                        </instructions>
                    </configuration>
                </plugin>


Copy & paste the given dependencies into your bundle pom.xml file.

<!-- Apache CXF Dependencies -->
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-bundle-minimal</artifactId>
            </dependency>
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-xjc</artifactId> 
            </dependency>

In bundle pom.xml file search for maven-bundle-plugin and replace that plugin with

          <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>integration_cq_cxf</Bundle-SymbolicName>
                    </instructions>
                </configuration>
            </plugin>

Also add given plugin into <plugins> tag in your bundle pom.xml file.

          <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>2.6.2</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceRoot>${basedir}/src/main/java</sourceRoot>
                            <wsdlRoot>${basedir}/src/main/wsdl</wsdlRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${basedir}/src/main/wsdl/ConvertTemperature.wsdl</wsdl>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>


This plugin is used to create Java files based on ConvertTemperature.wsdl file. This plugin have two paths. These are -
  1. <sourceRoot> </sourceRoot> It indicates the location where all the java class files will be generated. i.e. It will generate all java file under /<project>/src/main/java folder.
  2. <wsdlOption></wsdlOption> It indicates the location of wsdl file.
Note :  
If you don't want to save wsdl file locally then just write the url of your web service wsdl file in <wsdl> tag under <wsdlOption> tag in cxf-codegen-plugin so that your cxf-codegen-plugin looks like
                           .....   
                             <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>http://www.webservicex.net/ConvertTemperature.asmx?WSDL</wsdl>
                                </wsdlOption>
                            </wsdlOptions>
                          .....


Configuration part has been completed i.e. your code have all the required dependencies to interact with Apache CXF.

first check whether your code-generation-plugin is working or not. For checking go to your project location on your console and run- mvn generate-sources command. it will generate the required java proxy files based on the ConvertTemperature.wsdl file.

Note : 
Before running this command first delete all the file from your project target folders else it will not generate these classes.
Your project may need some other dependencies related to your code, make sure all those dependencies are also available.

After running this command you will see net.webservice package under /src/java.              

Let's write some code for testing what we have done. Create a JaxWsClientFactory.java class in any of your package in my case it is com.util; Copy this code into that file

package com.ig.util;

import org.apache.cxf.BusFactory;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class JaxWsClientFactory {


    private JaxWsClientFactory() { /* NO CONSTRUCTOR */}


    public static <T> T create(Class<T> ParentClass, String portUrl) {       

            return JaxWsClientFactory.create(ParentClass, portUrl, null, null);        
    }

    public static <T> T create(Class<T> ParentClass, String portUrl, String userName, String password) {


        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();

        try {
            jaxWsProxyFactoryBean.setServiceClass(ParentClass);
            jaxWsProxyFactoryBean.setAddress(portUrl);
            jaxWsProxyFactoryBean.setUsername(userName);
            jaxWsProxyFactoryBean.setPassword(password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) jaxWsProxyFactoryBean.create();
    }
}

For testing purpose- First create a interface named as ConvertTemperatureService.java under src/java/com/service directory. This interface having only one method as shown below -

package com.ig.integration;
public interface ConvertTemperatureService {
    double convertCelsiusToFahrenheit(double temperatureInCelsius);
}
Second create a service implementation class named as ConvertTemperatureImpl.java under  
src/java/com/service/impl directory as shown in previous fig.  and copy & paste this code.

package com.ig.integration.impl;

import com.ig.integration.ConvertTemperatureService;

import com.ig.util.JaxWsClientFactory;
import net.webservicex.ConvertTemperatureSoap;
import net.webservicex.TemperatureUnit;
import org.apache.cxf.BusFactory;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;

@Service

@Component(immediate = true,metatype = false,enabled = true)
public class ConvertTemperatureImpl implements ConvertTemperatureService{
    private ConvertTemperatureSoap convertTemperatureSoap;

    @Override

    public double convertCelsiusToFahrenheit(double valueToConvert) {
        double convertedTemperature = 0.0;
        try {
            convertTemperatureSoap = JaxWsClientFactory.create(ConvertTemperatureSoap.class,
                    "http://www.webservicex.net/ConvertTemperature.asmx");
            convertedTemperature = convertTemperatureSoap.convertTemp(valueToConvert,
                    TemperatureUnit.DEGREE_CELSIUS, TemperatureUnit.DEGREE_FAHRENHEIT);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return convertedTemperature;
    }

    public ConvertTemperatureSoap getInstance() {

        return convertTemperatureSoap;
    }

    @Activate

    protected void activate(final ComponentContext componentContext) {
        System.out.println("inside activate method");
    }

}

Here is the turning point you will get a exception in your stdout.log file.

exception is 
Caused by: java.lang.ClassNotFoundException: com.sun.xml.bind.v2.ContextFactory
at org.apache.sling.commons.classloader.impl.ClassLoaderFacade.loadClass(ClassLoaderFacade.java:127)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:192)

... 175 more



The reason for this is the illegal class loading in the OSGI context, and the fault is not on CXF but on the JAXB implementation, and we cannot fix this third-party implementation. By default, the Thread context class loader is not aware of OSGi and thus doesn't see any of the classes imported in the bundle. That's why loading the class fails.

Now just go to your factory classes (i.e. JaxWsClientFactory) and make some changes as shown below-

package com.ig.util;

import org.apache.cxf.BusFactory;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class JaxWsClientFactory {


    private JaxWsClientFactory() { /* NO CONSTRUCTOR */}


    public static <T> T create(Class<T> ParentClass, String portUrl) {

        ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(BusFactory.class.getClassLoader());
        try {
            return JaxWsClientFactory.create(ParentClass, portUrl, null, null);
        } finally {
            Thread.currentThread().setContextClassLoader(oldClassLoader);
        }
    }

    public static <T> T create(Class<T> ParentClass, String portUrl, String userName, String password) {

        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        try {
            jaxWsProxyFactoryBean.setServiceClass(ParentClass);
            jaxWsProxyFactoryBean.setAddress(portUrl);
            jaxWsProxyFactoryBean.setUsername(userName);
            jaxWsProxyFactoryBean.setPassword(password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) jaxWsProxyFactoryBean.create();
    }
}

Lines in bold format are the extra code written into your service class. The reason is that you have to change your class loader till you are dealing with web-service. first save the old class loader into some variable & at the time of completing the request set old class loader class.

Demo Project Location
You can clone this open source project from given git repository.
git@github.com:vietankur009/CQ_CXF_integration.git

Go to target Folder where you want this project and run git clone command as shown below.
git clone git@github.com:vietankur009/CQ_CXF_integration.git

Then you will see this project in target directory.
then run these commands -

cd integration_cq_cxf
mvn clean install -P autoInstallPackage

make sure your CQ instance is up and running.