CXF & Heroku, one more time…


One more Apache CXF and Heroku article to push Web services to the Java PaaS… In some previous articles I explained how to create JAXWS and JAXRS service by cloning/forking/whatever git repositories. This time it is almost the same but I created a maven archetype to generate tons of maven modules quickly and to integrate them in your maven-based projects (OK cloning a repository is faster, it does not download the entire Internet as Maven does…).

Let’s do it with a screen record to check how fast it is. With my poor Internet connection and some typos, I have something running on Heroku in less than 2 min 30…

Here are the commands used in the sample above:

The archetype source code is located at https://github.com/petalslink/petalscloud-maven-archetypes and deployed on OW2 repository. Once the project is generated from the archetype, one can add his own JAXWS-annotated services and associated Spring configuration (src/main/webapp/WEB-INF/beans.xml). Generated project is also available on github at https://github.com/chamerlingdotorg/maven-heroku-jaxws-sample.

Can it really be more easy?

Running JAXRS services in the Cloud in 5 minutes… or less!


Here is a really simple post about how to push REST services in the Cloud. Nothing really technical nor advanced, just some notes and sample using amazing tools CXF + Heroku…

Last time I was speaking about putting some SOAP Web services in the Cloud with Heroku, this time it is the same with REST services… The approach is exactly the same but it uses the JAXRS implementation provided by Apache CXF.

The REST service illustrates how to annotate the Java interface to returns JSON-based responses like:

Once implemented and configured (it uses Spring with the famous WEB-INF/beans.xml file), pushing it to Heroku is as simple as last time, nothing new here. Heroku needs a Procfile to start, and the Maven-based project is configured to generate what the Procfile needs: A shell file which launches a Jetty instance running Apache CXF and all the REST stuff.

The code is located at https://github.com/chamerling/heroku-cxf-jaxrs. You can run it locally to test before pushing to heroku:

  • mvn install
  • sh target/bin/webapp

Pushing your Web services in the Cloud in 5 minutes…


… or less! Heroku is defined as a « Cloud application platform ». I just want to redefine it to « Awesome Cloud application Platform ». So, this awesome platform provides a way to host and scale your application in the Cloud really easily with 3 or 4 commands…

Since I am currently working on my talk at #OW2Con 2011 (coming later this week) dealing with BPM, Services and the Cloud, I wanted to host some Web services on several places. I never had time to test Heroku but I just took this precious time today. After looking some examples, I created a Maven project template (no I do not have time to create an archetype, maybe there is one somewhere) which uses Jetty and Apache CXF to expose JAXWS annotated classes as Web services. So now, using heroku to freely expose your services is easy as:

  1.  Sign up to heroku
  2. Download the heroku client for your platform
  3. Clone/Fork the repository at https://github.com/chamerling/heroku-cxf-jaxws
  4. Add your own services
  5. Login to heroku ‘heroku auth:login
  6. Create the app on heroku ‘heroku create -s cedar
  7. Push your services to heroku ‘git push heroku master‘. There is a git hook somewhere which just automatically compile and start your application after you pushed it.
  8. Open your CXF services summary page ‘heroku open’
The default application name is some random one, you can rename it by using the ‘heroku rename yournewname‘ but in the current case I had an issue on the generated Web service endpoint name. So I suggest restarting your app after renaming (have a look to the ‘heroku ps‘ command).
That’s all, that’s quick!

Jouons avec WebServiceProvider…


Il y a des jours où exposer ses classes annotés avec @WebService n’est pas satisfaisant…
Pour moi ce jour c’est aujourd’hui: Il m’est impossible de marshaller mes beans en document DOM à cause d’un contexte JAXB tordu et d’une API qui ne prend que du DOM en entrée (OK bon ça c’est nul mais c’est pas de moi…). Qu’a cela ne tienne, il est temps d’utiliser les WebServiceProvider puisque ils vont me permettre de directement récupérer mon message SOAP sous forme de document… Un peu moins straight forward comme approche mais qui peut convenir dans certains cas. Comme les exemples ne courent pas le Web, regardons ce que l’on peut faire pour s’en sortir…

(Tout le code source de cet article est disponible sur Github : http://github.com/chamerling/chamerling.org-samples/tree/master/cxf-serviceprovider-072011)

Une première solution simple est d’implémenter javax.xml.ws.Provider, de rajouter deux/trois annotations et le tour est quasiment joué:

package org.chamerling.blog.cxf.serviceprovider;

import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Provider;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceProvider;

/**
* @author chamerling
*
*/
@WebServiceProvider
@ServiceMode(value = javax.xml.ws.Service.Mode.MESSAGE)
public class SimpleServiceProvider implements Provider {

/*
* (non-Javadoc)
*
* @see javax.xml.ws.Provider#invoke(java.lang.Object)
*/
@Override
public SOAPMessage invoke(SOAPMessage in) {
return null;
}
}

Reste à l’exposer avec CXF en utilisant org.apache.cxf.jaxws.JaxWsServerFactoryBean. Cette approche simpliste a le mérite de marcher, il ne reste plus qu’a manipuler les SOAPMessage dans l’implémentation de la méthode invoke.

Besoin de l’opération qui est appelée? Ajoutons javax.xml.ws.WebServiceContext en tant que javax.annotation.Resource. Ce contexte sera automatiquement rempli pour nous par CXF et accessible dans le corps de la méthode invoke. Par exemple, on peut travailler de la sorte:

/**
 *
 */
package org.chamerling.blog.cxf.serviceprovider;

import javax.annotation.Resource;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Provider;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;

/**
 * @author chamerling
 *
 */
@WebServiceProvider
@ServiceMode(value = javax.xml.ws.Service.Mode.MESSAGE)
public class SimpleServiceProvider implements Provider {

	@Resource
	WebServiceContext wsContext;

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.xml.ws.Provider#invoke(java.lang.Object)
	 */
	@Override
	public SOAPMessage invoke(SOAPMessage in) {
		System.out.println("Operation : " + getOperation());
		System.out.println("Message In :");
		try {
			in.writeTo(System.out);
		} catch (Exception e) {
			// bad
		}
		System.out.println();
		return null;
	}

	private QName getOperation() {
		QName result = null;
		if (wsContext != null && wsContext.getMessageContext() != null) {
			Object o = wsContext.getMessageContext().get(
					MessageContext.WSDL_OPERATION);
			if (o != null && o instanceof QName) {
				result = (QName) o;
			}
		}
		return result;
	}
}

Et invoquer le service:

package org.chamerling.blog.cxf.serviceprovider;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

import junit.framework.TestCase;

/**
 * @author chamerling
 *
 */
public class SimpleServiceTest extends TestCase {

	public void testExpose() throws Exception {
		String url = "http://localhost:9999/foo/bar/SimpleService";
		final JaxWsServerFactoryBean ssf = new JaxWsServerFactoryBean();
		ssf.setAddress(url);
		ssf.setServiceBean(new SimpleServiceProvider());
		ssf.create();

		HelloService client = getClient(url);
		client.sayHello("Rock N Roll!");
	}

	/**
	 * @param url
	 * @return
	 */
	private HelloService getClient(String url) {
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.setAddress(url);
		factory.setServiceClass(HelloService.class);
		Object client = factory.create();
		return HelloService.class.cast(client);
	}
}

Dans ce cas, on a une opération affichée qui est {http://serviceprovider.cxf.blog.chamerling.org/}invoke et le message SOAP

Rock N Roll!

Pas très convaincant pour l’opération avec cette approche… Le mieux est de pousser un peu plus loin et partir du contrat de service (WSDL) de notre Provider. CXF permet de le spécifier via ses factories lors de la construction du service. Cette utilisation plus avancée est détaillée en partant de org.chamerling.blog.cxf.serviceprovider.CXFExposer. L’approche utilisée dans CXFExposer permet aussi de cacher toute la tambouille JAXWS à l’utilisateur, au final il a besoin d’implementer seulement org.chamerling.blog.cxf.serviceprovider.Service

package org.chamerling.blog.cxf.serviceprovider;

import javax.xml.namespace.QName;

import org.w3c.dom.Document;

/**
 * @author chamerling
 *
 */
public interface Service {

    /**
     * Get the WSDL description
     *
     * @return
     */
    String getWSDLURL();

    /**
     * Get the service URL ie where to publish it...
     *
     * @return
     */
    String getURL();

    QName getEndpoint();

    QName getInterface();

    QName getService();

    /**
     * Invoke the service
     *
     * @param request
     * @param action
     */
    Document invoke(Document in, QName operation) throws ServiceException;

}

Jouons avec le Petals Kernel et Apache CXF


Oui, à chacun sa façon de s’amuser… Je viens d’ajouter une feature dans le Petals Distributed Service Bus qui me trottait dans la tête depuis un petit moment: Pouvoir invoquer des services techniques (distants ou pas) du coeur du DSB en utilisant la couche de transport naturellement utilisée pour invoquer des services métiers (les services hostés par le bus ou liés à celui ci). Autrement dit, imaginons que j’ai un service technique qui tourne sur un noeud et que depuis un autre noeud, je dispose d’un client pour l’invoquer de façon simple mais aussi totalement transparente.

Ce que l’on pouvait faire jusqu’à présent, c’est utiliser l’API Web service exposée par le DSB pour invoquer le service pour peux que le service soit annoté comme il faut (pour rappel, cette API Web service utilise Apache CXF pour exposer automatiquement les composants Fractal qui sont annotés avec JAXWS). Bien, mais par défaut on utilise deux ports pour les communications inter noeuds: Le port utilisé par Apache CXF, et l’autre par la couche de transport du DSB (en fait c’est le cas dans Petals ESB, mais dans le DSB, la couche de transport étant par défaut une implémentation en Web service, on utilise le même port pour les deux). En allant un peu plus dans les détails, même si on utilise le même port pour les deux modules dans le DSB, ceci n’est plus vrai si une nouvelle implémentation de la couche de transport utilise autre chose que CXF (pour info, dans le DSB, une implémentation de la couche de transport en XMPP existe aussi). C’est donc la qu’on arrive a la solution d’utiliser le canal de communication du DSB pour toute invocation entre noeuds, comme on sait déjà échanger des messages avec le DSB, pourquoi s’en priver?

C’est bien beau de vouloir exposer des services, des composants Fractal, ou quoi que ce soit, si on veut rester en Java et ne pas avoir besoin de se triturer le peu de cerveau qu’il nous reste, autant penser à utiliser tout ou partie d’une solution qui existe deja. En allant dans ce sens, comme on doit au final avoir une sorte de serialization des messages, autant utiliser quelque chose qui sait deja le faire, j’ai nommé le couple inséparable JAXB+JAXWS. Ceci impliquera seulement d’annoter ses interfaces et de créer des types propres et JAXB-aware mais au final, je ne pense pas que cela soit une contrainte forte… C’est bien beau tout cela mais JAXB + JAXWS tout seul ca ne fait pas grand chose. C’est la qu’entre en jeu Apache CXF. Puisque l’utilisation de JAXWS est simple avec CXF, autant s’intéresser un peu à la bete. Pour le coup cà tombe bien, dans CXF il y a deja une notion de transport multiple: HTTP, XMPP, JMS et si on regarde bien on a même JBI (les potes de chez JonAS ont d’ailleurs implémenté un transport CXF utilisant les EJB ou un truc J2EE dans le style si ma mémoire est bonne). Tiens donc, il y a un transport JBI? Ca tombe (presque) bien, le DSB est basé sur Petals ESB qui est JBI compliant. Oui mais en fait non. J’aurais presque pu l’utiliser mais le but est d’implémenter quelque chose d’assez ‘JBI independant’. Le tout est maintenant d’implémenter un nouveau transport pour le DSB et grace au code source de CXF et à cette page http://cxf.apache.org/custom-cxf-transport.html on comprend assez bien comment faire. Dans la terminologie CXF, le client est le ‘conduit’ et le serveur est la ‘destination’. Il suffit donc d’implémenter nos propres conduit et destination pour pouvoir échanger des messages avec CXF.

Une fois CXF configuré avec nos conduit et destination, ou plutôt une fois la bonne transport factory implémentée, on devrait pouvoir faire quelque chose du style:

// 1. Initialize CXF Bus with factory
Bus bus = BusFactory.getDefaultBus();
DestinationFactoryManager dfm = bus.getExtension(DestinationFactoryManager.class);
DSBTransportFactory petalsTransportFactory = new DSBTransportFactory();
petalsTransportFactory.setBus(bus);
petalsTransportFactory.registerWithBindingManager();

// 2. Start service
System.out.println("Starting CXF server...");
JaxWsServerFactoryBean sf = new JaxWsServerFactoryBean();
sf.setAddress("dsb://host/serviceA");
sf.setServiceClass(HelloService.class);
sf.setServiceBean(new HelloService() {
    public String sayHello(String input) throws PEtALSWebServiceException {
        System.out.println("SAY HELLO INVOKED!");
        return pre+input;
    }
});
sf.create();

// 3. Create the client and invoke
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setAddress("dsb://host/serviceA");
factory.setServiceClass(HelloService.class);
HelloService hello = (HelloService) factory.create();
String out = null;
try {
    out = hello.sayHello(in);
} catch (PEtALSWebServiceException e) {
}

Je passe les détails d’implémentation (que j’ai eu le plaisir de faire dans un bon vieux TGV en panne entre Montpellier et Bruxelles) pour le moment, mais ce qui se passe se résume à:

  1. On configure CXF en lui ajoutant notre Transport Factory. Au niveau de l’implémentation, il y a ce qu’il faut pour que lorsque une client ou une server factory est configurée avec une adresse qui commence par ‘dsb://’, ce soit notre DSBFactory qui soit automatiquement appelée par CXF.
  2. La création du service se fait comme si on créait un Web service sur http sauf que là c’est un ‘Web service sur DSB’.
  3. Idem que pour 2 mais on génère un client qui nous permettra d’appeler le service créé auparavant.

En résultat, pour peu que le client (en 3) et le serveur (en 2) soient exécutés sur des noeuds séparés, le DSB se chargera d’acheminer la requête et la réponse comme il faut entre les acteurs et ceci de façon complètement transparente. Au niveau de l’implémentation coté kernel du DSB (ceci vaut aussi pour Petals ESB), il suffit de connaître quand même un peu le coeur pour créer des fakes de composants JBI en farfouillant dans les APIs disponibles, de savoir jouer avec le DeliveryChannel, le ComponentContext, le NormalizedMessageRouter, ajouter quelques modules de routage, savoir recevoir des messages et y répondre, et surtout implémenter le Conduit et la Destination CXF en s’inspirant de ce qui existe déjà et en mettant les doigts dans le cambouis. Pour les curieux, je vous conseille de jeter un oeil au code de CXF assez compréhensible et pas mal fait du tout.

Enfin pour étendre cela aux services techniques du coeur du DSB, il suffit, au runtime, de scanner les services disponibles offerts par les composants du kernel et automatiquement créer les services comme décrit dans l’étape 2. Chose que l’on sait déjà faire dans le DSB via le scan pour exposer les services en Web service sur HTTP avec CXF (oui toujours lui, avant je m’en prenais a Axis2…).

Et voila donc une belle et simple façon de simplifier la vie du développeur du DSB (c’est à dire moi). Il va être maintenant possible d’invoquer n’importe quoi de n’importe où avec une API Java. Pour peu que l’on plugge quelques modules au bon endroit, on peut imaginer faire des choses assez sympa: Le Cloud en fait partie.

Note: Le code sera disponible d’ici la fin de la semaine sur le SVN du DSB. Surement sous https://svn.petalslink.com/svnroot/trunk/research/commons/dsb/modules/dsb-kernel, je mettrais a jour l’article en fonction…